ls /dir |while read dirname; do cp -v /dir/"$dirname"/file "$dirname"-file ; done
Não tenho certeza de como expressar isso, e talvez seja por isso que não consigo encontrar nada, mas quero reutilizar os valores enumerados por um caractere curinga em um comando. Isso é possível?
Cenário:
$ ls /dir
O conteúdo de / dir são diretórios 1, 2 e 3.
1 2 3
$ cp /dir/*/file .
Resulta no arquivo sendo copiado de / dir / 1 / dir / 2 e / dir / 3 para aqui.
O que eu gostaria de fazer é copiar os arquivos para um novo nome de destino com base na expansão de curingas.
$ cp /dir/*/file ???-file
Resultaria no arquivo / dir / * / sendo copiado para 1 arquivo, 2 arquivos e 3 arquivos. O que eu não consigo descobrir é a porção ???
para dizer ao BASH que eu quero usar os valores expandidos do curinga.
Usando o curinga nas redes de destino, um erro de cp: cp: target '* -file' não é um diretório. Há algo mais no bash que pode ser usado aqui?
O comando de localização tem {}
para usar com -exec
, que é semelhante ao que estou procurando acima.
Meu primeiro pensamento é que você provavelmente está tornando este problema muito complicado. No entanto, é solucionável.
Você pode fazer algo assim com um loop bash maluco:
for i in /dir/*/file
do
j=${i%/file}
k=${j#/dir/}
cp $i %k-file
done
que usa alguns operadores de string bash para extrair o número do diretório de origem.
Eu garanto que existem melhores maneiras de fazer isso, foi exatamente isso que me veio à mente.
zsh% autoload zmv # should be done for you, noting it just in case
zsh% zmv -C '(*)/file' '$1-file'
zmv é uma função do shell, geralmente usada via alias mmv='noglob zmv -W'
para uma invocação ainda mais fácil, mas neste caso você quer o uso regular, além de -C copiar em vez de mover.
Como Phil disse, você precisará iterar sobre os elementos do curinga expandido; extraindo o bit com o qual você se importa (no seu exemplo, o nome do diretório sob dir /) e, em seguida, atuando nesse valor extraído.
Eu tenho uma maneira diferente e rápida de fazer isso abaixo. O erro óbvio é que isso vai quebrar se houver espaços em nomes de arquivos, mas esse não é o único bug.
andromeda:tmp polleyj$ find dir/ ?-file
dir/
dir//1
dir//1/a
dir//1/b
dir//1/c
dir//2
dir//2/d
dir//2/e
dir//2/f
dir//3
dir//3/g
dir//3/h
dir//3/i
1-file
2-file
3-file
andromeda:tmp polleyj$ for f in dir/*; do
> i=$(basename $f)
> mv dir/${i}/* ${i}-file/
> done
andromeda:tmp polleyj$ find dir/ ?-file
dir/
dir//1
dir//2
dir//3
1-file
1-file/a
1-file/b
1-file/c
2-file
2-file/d
2-file/e
2-file/f
3-file
3-file/g
3-file/h
3-file/i
andromeda:tmp polleyj$
Na maioria das distros (usando o renomear ) do util-linux:
rename /file -file /dir/*/file
No Debian e no Ubuntu (culpe o Debian):
rename.ul /file -file /dir/*/file
Ou usando a renomeação do Perl (no Debian e no Ubuntu, não sei onde encontrá-lo em outras distros):
rename 's,(\d+)/file,$1-file,' */file
Não é bonito:
find -type f -exec sh -c 'cp "{}" "$(basename "$(dirname "{}")")-$(basename "{}")"' \;
Se eu entendi, o mcp está fazendo o que você quer.
Dica: o mcp pertence ao pacote mmv .
Sua situação:
$ tree . ├── dir │ ├── 1 │ │ └── foo │ ├── 2 │ │ └── bar │ └── 3 │ └── baz └── dst 5 directories, 3 files
A solução:
$ mcp -v 'dir/*/*' 'dst/#1-#2' dir/1/foo -> dst/1-foo : done dir/2/bar -> dst/2-bar : done dir/3/baz -> dst/3-baz : done $ tree . ├── dir │ ├── 1 │ │ └── foo │ ├── 2 │ │ └── bar │ └── 3 │ └── baz └── dst ├── 1-foo ├── 2-bar └── 3-baz 5 directories, 6 files
Eu acho que deve ser mais auto-explicativo
O que acontece aqui é que o mcp leva um de e um argumento para . O argumento de pode conter padrões de caractere curinga de shell (* ,?) que correspondem a um conjunto de arquivos. Essas correspondências são referenciadas novamente no padrão para de forma ordenada como # 1, # 2, # 3 e assim por diante.