Como copiar uma pasta recursivamente de forma idempotente usando cp?
On Dezembro 1, 2020 by adminQuando eu uso
cp -R inputFolder outputFolder
o resultado é dependente do contexto :
- se
outputFolder
não existir, ele existirá ser criado, e o caminho da pasta clonada seráoutputFolder
. - se
outputFolder
existir, o clone criado será sejaoutputFolder/inputFolder
Isso é horrível , porque eu quero criar algum script de instalação, e se o usuário executá-lo duas vezes por engano, ele terá outputFolder
criado na primeira vez e, na segunda execução, todo o material será ser criado novamente em outputFolder/inputFolder
.
- Quero sempre o primeiro comportamento: criar um clone ao lado do original (como irmão).
- Desejo usar
cp
para ser portátil (por exemplo MINGW nãorsync
enviado) - Eu verifiquei
cp -R --parents
, mas isso recria o caminho em toda a árvore de diretórios (então o clone não seráoutputFolder
, massome/path/outputFolder
) -
--remove-destination
ou--update
no caso 2 não mudar nada, ainda as coisas são copiadas emoutputFolder/inputFolder
É há uma maneira de fazer isso sem primeiro verificar a existência de outputFolder
(se a pasta não existir, então …) ou usando rm -rf outputFolder
?
Qual é a maneira combinada e portátil do UNIX de fazer isso?
Comentários
Resposta
Use isto em seu lugar:
cp -R inputFolder/. outputFolder
Isso funciona exatamente da mesma forma que, digamos, cp -R aaa/bbb ccc
funciona: se ccc
não existir, então ele ” s criados como uma cópia de bbb
e seu conteúdo; mas se ccc
já existir, ccc/bbb
é criado como a cópia de bbb
e seu conteúdo .
Para quase todas as instâncias de bbb
, isso dá o comportamento indesejável que você notou na sua pergunta. No entanto, nesta situação específica, o bbb
é apenas .
, então aaa/bbb
é realmente aaa/.
, que por sua vez é realmente apenas aaa
, mas com outro nome. Portanto, temos estes dois cenários:
-
ccc
não existe:O comando
cp -R aaa/. ccc
significa” criarccc
e copiar o conteúdo deaaa/.
paraccc/.
, ou seja, copieaaa
paraccc
. -
ccc
existe:O comando
cp -R aaa/. ccc
significa” copiar o conteúdo deaaa/.
paraccc/.
, ou seja, copieaaa
paraccc
.
Comentários
- Huh, esse ‘ é novo para mim, mas parece funcionar! Está documentado em algum lugar?
- Eu ‘ testei o
inputFolder/.
em vez deinputFolder
e realmente funciona para mim no Windows / Git Bash. (No entanto, não funciona com apenas um/
final, preciso também do ponto após a barra).Eu dei +1 porque ‘ é interessante, embora ‘ seja um pouco complicado demais para usá-lo em código público:) - @IlmariJaronen não ‘ não precisa ser documentado explicitamente. Siga a explicação e você ‘ verá como ele simplesmente ” segue as regras ” .
Resposta
Não copie a pasta, copie apenas o conteúdo:
## Create the target directory. The -p suppresses error messages ## if the directory already exists mkdir -p outputFolder ## Copy the contents recursively, this will not recreate the parent cp -R inputfolder/* outputfolder/
Desta forma, você garante que o diretório de destino seja criado na primeira vez que o script é executado e evita o problema ao executá-lo uma segunda vez.
Chris Down aponta muito corretamente que no bash, isso irá ignorar arquivos cujo nome comece com .
. Para evitar isso , você pode executar shopt -s dotglob
antes de executar o comando acima.
Ambos -p
para mkdir
e -R
para cp
são definidos por POSIX, portanto, devem ser perfeitamente portáveis.
Comentários
- Observação: isso não ‘ t copiar arquivos começando com
.
. No bash, você pode usardotglob
para isso. - Observe que este método provavelmente falhará se o número de entradas de diretório em inputfolder for grande, devido à expansão glob pode exceder o comprimento máximo da linha de comando.
Resposta
Experimente o -T
opção para cp
. Isso existe no GNU coreutils cp
versão 8.22; pode não ser portátil fora disso.
Comentários
- Sim, parece que isso é exatamente o que eu queria:
cp -T
(cp --no-target-directory
). unix.stackexchange.com/questions/94831/… Infelizmente, minha versão decp
é muito mais antigo. - +1 Usei apenas hoje.
-u
é uma ótima opção para adicionar para torná-lo preguiçoso.
Resposta
Você também pode usar rsync
:
rsync -uav inputFolder/ outputFolder/
(observe as barras, especialmente após a primeira )
Resposta
Você pode usar a opção -t
de cp
comando como:
cp -R inputFolder -t outputFolder
agora, se a pasta de destino não existir, ele gerará um erro:
cp: failed to access ‘outputFolder’: No such file or directory
O comando da nota acima irá copiar inputFolder
junto com seu conteúdo (e não apenas o conteúdo dentro dele)
se você quiser copiar apenas o conteúdo de inputFolder
fica um pouco complicado (já que você deve ter cuidado ao usar o shell globbing ao usar o asterisco *)
cp -R -t outputFolder/ -- inputFolder/*
agora, se a pasta de destino não existir, ocorrerá um erro:
cp: failed to access ‘outputFolder’: No such file or directory
funciona com cp (GNU coreutils) 8.23
Resposta
Na minha opinião, não há nada mais simples e mais portátil do que rm -rf outputFolder
. Então, eu sempre fico com isso. Entendo que sua pergunta é diferente, mas acho que essa é a prática recomendada.
Comentários
- Isso falha se houver permissões ou propriedades específicas no alvo que precisava ser mantido. Ou se fosse um link simbólico.
- A questão quer que o resultado de
cp
seja o mesmo, tanto quando o diretório não existe quanto quando ele existe. Então, do que você está falando? - O comando
cp
dado pelo OP não ‘ mantém as permissões (ou carimbos de data / hora).
mv
mova uma coleção de arquivos para uma única coleção regular?rsync
?cp
, canalizar entre doistar
comandos é uma boa maneira confiável de copiar árvores.rsync
enviado.tar -C input -cf - . | tar -C output -xf -
funciona.-C
muda o diretório de trabalho, mas não é uma opção padrão, então se ‘ não for compatível, você precisa executar os dois tars em os diretórios de trabalho certos para começar, por exemplo( cd input && tar -cf - . ) | ( cd output && tar -xf - )
No entanto, se você ‘ estiver lidando com o Windows, eu usaria roaima ‘ a resposta .