Como copiar vários arquivos por curinga?

42

Eu tenho uma pasta com vários arquivos nela ABC. * (Existem aproximadamente 100 desses arquivos). Quero duplicar todos eles em novos arquivos com nomes começando com DEF. *

Então eu quero

ABC.Page1
ABC.Page2
ABC.Topic12
...etc

copiado para

DEF.Page1
DEF.Page2
DEF.Topic12
...etc

Qual é a maneira mais simples de fazer isso com um comando em lote (no BASH ou similar)? Estou pensando em algo que envolva sed ou awk ou xargs, mas estou tendo dificuldades para descobrir a sintaxe. Eu poderia escrever um script Python, mas acho que provavelmente existe uma solução de linha de comando que não é muito complicada.

Polvo
fonte
Um venerável mcgerenciador de arquivos tem um suporte mais amplo para esse tipo de renomeação em massa.
oakad
@oakad tartambém não é uma galinha da primavera. Veja minha resposta para um exemplo.
precisa saber é o seguinte

Respostas:

41

Que tal algo assim no bash:

for file in ABC.*; do cp "$file" "${file/ABC/DEF}";done

você pode testá-lo colocando echo na frente do comando cp:

for file in ABC.*; do echo cp "$file" "${file/ABC/DEF}";done
Magnus
fonte
perfeito. ainda mais simples do que eu imaginava.
Octopus
Vai ser bom o suficiente na maioria dos casos, mas verificar com o eco é crucial aqui. Nunca faça isso em um script . Para mais informações, leia armadilhas festança
pawel7318
@ pawel7318, o link que você deu, embora seja um excelente conselho, parece não se aplicar ao comando fornecido acima. Os únicos casos extremos que vejo com este comando são: (1) Não verifica se o arquivo de destino já existe nem se o arquivo de destino já existe como um diretório (que copiaria o arquivo para o diretório, preservando seu nome atual), e (2) se houver tantos arquivos que a expansão glob não possa caber em uma única linha de comando.
Curinga
11
Eu entendi: cp /usr/local/lib/libboost* /usr/local/lib/libboost*com a verificação de eco.
Tomáš Zato
8

Para fazer isso de maneira eficiente com um grande número de arquivos, é melhor evitar o início de um cpprocesso diferente para cada um. Uma maneira seria copiar e renomeá-los usando prename( renameestá vinculado a isso por padrão nas distribuições baseadas no Debian). Usando isso e o Linux mktemp:

tmp=$(mktemp -d --tmpdir=.)
cp ABC.* "$tmp"
prename "s:$tmp/ABC:DEF:" "$tmp/"*
rmdir "$tmp"

Atualizar

Na verdade, paxpode ser uma maneira melhor de ir aqui:

pax -rws '/^ABC/DEF/' ABC.* .

Infelizmente, apesar de paxserem POSIX e Linux Standard Base, poucas distribuições atualmente o incluem por padrão.

Graeme
fonte
toque agradável usando o: como separador para evitar fugas.
Clayton Stanley
5

tar fará isso por você muito rápido.

TESTE

Primeiro, criei 2 diretórios e 10 arquivos:

% mkdir test1 test2 ; cd test1
% for n in `seq 1 10` ; do touch ABC.file$n ; done
% ls

> ABC.file1   ABC.file2  ABC.file4  ABC.file6  ABC.file8
> ABC.file10  ABC.file3  ABC.file5  ABC.file7  ABC.file9

Então eu os copiei:

% tar -cf - ./* |\ 
    tar -C../test2 --transform='s/ABC/DEF/' -xf -
% ls ../test2

> DEF.file1   DEF.file2  DEF.file4  DEF.file6  DEF.file8
> DEF.file10  DEF.file3  DEF.file5  DEF.file7  DEF.file9

TRANSFORMAR

Portanto, o GNU taraceitará um sed --transform=EXPRESSIONpara renomear arquivos. Isso pode até renomear apenas alguns dos arquivos. Por exemplo:

% tar -cf - ./* |\ 
    tar -C../test2 --transform='s/ABC\(.*[0-5]\)/DEF\1/' -xf -
% ls ../test2

> ABC.file6  ABC.file8  DEF.file1   DEF.file2  DEF.file4
> ABC.file7  ABC.file9  DEF.file10  DEF.file3  DEF.file5

Então essa é uma vantagem.

CORRENTE

Considere também que são apenas dois tarprocessos - e que não serão alterados independentemente da sua contagem de arquivos.

tar | tar

taré tão otimizado quanto você gostaria que fosse. Isso nunca terá contagens de argumentos de problemas ou processos filhos em fuga. Isso é apenas A> B feito.

ARGUMENTOS

Eu uso 7 argumentos distintos combinados entre meus dois tarprocessos aqui. O mais importante é listado aqui primeiro:

-stdout / stdin - informa tarque transmitirá sua entrada ou saída para a stdin/stdoutqual interpretará corretamente, dependendo de estar construindo ou extraindo um arquivo morto.

-ccreate - isto diz tarpara construir o arquivo. O próximo argumento tarespera é ...

-farquivo - especificamos que tartrabalhará com um fileobjeto, em vez de um dispositivo de fita ou qualquer outra coisa. E o arquivo com o qual ele estará trabalhando, como observado acima, é stdin/stdout- em outras palavras, nosso |pipe.

./*todos os $ PWD / arquivos - não há muito a explicar aqui, exceto que o argumento de arquivamento vem primeiro, -então ./*.

... e do outro lado da |pipe...

-Calterar diretório - informa tarque ele precisa mudar para o diretório especificado antes de executar qualquer outra ação, tão efetivamente cd ../test2antes da extração.

--transform='s/ed/EXPR/'- como já foi mencionado, isso foi renomeado. Mas os documentos indicam que ele pode ter qualquer sedexpressão ou //flag.

-xextrair - depois de taralterar nosso diretório de destino e receber nossas instruções de renomeação, instruímo-lo a começar a extrair todos os arquivos em seu diretório atual a partir do -f - |pipearquivo morto. Sem mistério.

mikeserv
fonte
11
Aqui está a paxsolução para esta pergunta. Gostaria de acrescentar que a minha resposta, mas eu acho que é mais adequado aqui -pax -rws '/^ABC/DEF/' ABC.* .
Graeme
@Graeme - woah - é muito melhor! Cara, também supera renomear pérolas! Você deve colocar isso na sua resposta - paxé POSIX especificado - daí o pax.
precisa saber é o seguinte
paxpode ser POSIX, mas raramente é instalado por padrão, infelizmente.
Graeme #
4

Se você não se importa em usar uma ferramenta menos padrão, recomendo usar mmv (movimento em massa), que foi escrito para esse tipo de tarefa. Usando mcp(cópia em massa), que faz parte do pacote, você pode simplesmente fazer

mcp "ABC.*" "DEF.#1"

Não é exatamente fácil encontrar pacotes de download. O pacote Debian atual (que o Arch também usa no AUR) para compilar em casa pode ser encontrado aqui .

Jonny
fonte
instala no ubuntu 15.10 fora da caixa,apt-get install mmv
Thufir 14/02
2

No zsh, coloque as seguintes linhas no seu .zshrc:

autoload -U zmv
alias zmv='noglob zmv'
alias zcp='zmv -C'
alias zln='zmv -L'

Em seguida, na linha de comando, você pode executar

zcp 'ABC(.*)' 'DEF$1'

ou simplesmente

zcp -W ABC.* DEF.*

Em outro shell, desde que você tenha o zsh instalado:

zsh -c 'autoload zmv; noglob zmv -CW ABC.* DEF.*'
Gilles 'SO- parar de ser mau'
fonte
1

Você pode fazer isso:

for i in ABC.*; do cp {ABC,DEF}."${i#*.}"; done
Sara Fauzia
fonte
Talvez haja algo que eu não esteja vendo, mas como estou definindo vários destinos? Estou usando o loop for para obter cada um. Eu testei o código de antemão, e funcionou conforme o esperado, para agradecer sua explicação.
Sara Fauzia 02/02
Isso é ruim, eu interpretei mal sua resposta. :-)
Chris Down
Como devo ler a parte "${i#*.}"?
dumbledad