Como copiar uma pasta recursivamente de uma maneira idempotente usando cp?

41

Quando uso

cp -R inputFolder outputFolder

o resultado é dependente do contexto :

  1. se outputFolder não existir, ele será criado e o caminho da pasta clonada será outputFolder .
  2. se outputFolder existir, o clone criado será outputFolder/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, todos o material será criado novamente em outputFolder/inputFolder .

  • Eu quero sempre o primeiro comportamento: criar um clone ao lado do original (como irmão).
  • Desejo usar o cp como portável (por exemplo, o MINGW não tem rsync enviado)
  • Eu verifiquei cp -R --parents , mas isso recria o caminho até a árvore de diretórios (para que o clone não seja outputFolder , mas some/path/outputFolder )
  • --remove-destination ou --update no caso 2 não alteram nada, ainda assim as coisas são copiadas para outputFolder/inputFolder

Existe uma maneira de fazer isso sem verificar primeiro a existência do outputFolder (se a pasta não existir, então ...) ou usando rm -rf outputFolder ?

Qual é a maneira UNIX, portátil e acordada de fazer isso?

    
por jakub.g 09.09.2015 / 18:00

6 respostas

49

Use isso:

cp -R inputFolder/. outputFolder

Isso funciona exatamente da mesma maneira que, digamos, cp -R aaa/bbb ccc funciona: se ccc não existir, ele será criado como uma cópia de bbb e seu conteúdo; mas se ccc já existir, então ccc/bbb será criado como a cópia de bbb e seu conteúdo.

Para quase todas as instâncias de bbb , isso indica o comportamento indesejável que você observou na sua pergunta. No entanto, nessa situação específica, o bbb é apenas . , então aaa/bbb é realmente aaa/. , que, por sua vez, é apenas aaa , mas com outro nome. Então, temos esses dois cenários:

  1. ccc não existe:

    O comando cp -R aaa/. ccc significa "criar ccc e copiar o conteúdo de aaa/. em ccc/. , ou seja, copiar aaa em ccc .

  2. ccc existe:

    O comando cp -R aaa/. ccc significa "copiar o conteúdo de aaa/. em ccc/. , ou seja, copiar aaa em ccc .

por 09.09.2015 / 21:30
18

Não copie a pasta, apenas copie 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/

Dessa 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 muito corretamente aponta que no bash, isso ignorará os arquivos cujo nome começa 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 pelo POSIX, portanto, isso deve ser perfeitamente portátil.

    
por 09.09.2015 / 19:16
11

Experimente a opção -T para cp . Isto existe no GNU coreutils cp versão 8.22; pode não ser portável fora disso.

    
por 09.09.2015 / 18:12
2

Você também pode usar rsync :

rsync -uav inputFolder/ outputFolder/

(observe as barras, especialmente depois da primeira)

    
por 10.09.2015 / 21:19
0

Na minha opinião, não há nada mais simples e mais portátil que rm -rf outputFolder . Então, eu sempre fico com isso. Eu entendo que sua pergunta é diferente, mas acho que essa é a melhor prática.

    
por 22.09.2015 / 17:01
0

Você pode usar a opção -t do comando cp como:

cp -R inputFolder -t outputFolder

agora, se a pasta de destino não existir, isso causará um erro:

cp: failed to access ‘outputFolder’: No such file or directory
O comando

note acima copiará inputFolder junto com seu conteúdo (e não apenas o conteúdo dentro dele)

se você deseja copiar apenas o conteúdo de inputFolder , fica um pouco complicado (já que você precisa ter cuidado ao usar o shell globbing enquanto usa asterisco *)

cp -R  -t outputFolder/ -- inputFolder/*

agora, se a pasta de destino não existir, isso causará um erro:

cp: failed to access ‘outputFolder’: No such file or directory

funciona com cp (GNU coreutils) 8.23

    
por 22.09.2015 / 17:03

Tags