Como forçar 'cp' a sobrescrever o diretório em vez de criar outro dentro?

107

Estou tentando escrever um script Bash que substituirá um diretório existente. Tenho um diretório foo/e estou tentando substituí bar/-lo. Mas quando eu faço isso:

cp -Rf foo/ bar/

um novo bar/foo/diretório é criado. Eu não quero isso. Existem dois arquivos em foo/; ae b. Também há arquivos com os mesmos nomes bar/. Quero que foo/ae foo/bsubstitua bar/ae bar/b.

saketrp
fonte

Respostas:

122

Você pode fazer isso usando a -Topção em cp.
Consulte a página do manual para cp.

-T, --no-target-directory
    treat DEST as a normal file

Portanto, de acordo com seu exemplo, a seguir está a estrutura do arquivo.

$ tree test
test
|-- bar
|   |-- a
|   `-- b
`-- foo
    |-- a
    `-- b
2 directories, 4 files

Você pode ver a diferença clara quando você usa -vpara Verbose.
Quando você usa apenas a -Ropção.

$ cp -Rv foo/ bar/
`foo/' -> `bar/foo'
`foo/b' -> `bar/foo/b'
`foo/a' -> `bar/foo/a'
 $ tree
 |-- bar
 |   |-- a
 |   |-- b
 |   `-- foo
 |       |-- a
 |       `-- b
 `-- foo
     |-- a
     `-- b
3 directories, 6 files

Quando você usa a opção, -Tela sobrescreve o conteúdo, tratando o destino como um arquivo normal e não como um diretório .

$ cp -TRv foo/ bar/
`foo/b' -> `bar/b'
`foo/a' -> `bar/a'

$ tree
|-- bar
|   |-- a
|   `-- b
`-- foo
    |-- a
    `-- b
2 directories, 4 files

Isso deve resolver seu problema.

Saurabh Meshram
fonte
22
caso alguém se atrapalhe com isso, não funcionará com OSX cp developer.apple.com/library/mac/documentation/Darwin/Reference/…
dnfehren
9
Não está claro se esta resposta é o que o OP está procurando, embora os exemplos dados acima mascarem o problema ... Com a opção -T, os arquivos que estão em um destino existente ( bar/), mas não na origem ( foo/) serão deixados em vigor, portanto, isso não é o que a maioria das pessoas consideraria uma substituição completa do diretório. ie. se bar/bazjá existisse, ainda existiria depois ...
robo
1
Esta resposta responde à pergunta op, mas não aborda o caso de se o destino já existe e você deseja remover o conteúdo que ele contém, mas o diretório de origem não. Este não é um comportamento esperado de copiar arquivos de um lugar para outro. Ele apenas sobrescreve no destino coisas que também estão na origem, não toca em nada no destino que não esteja na origem. Você pode limpar a pasta de destino adicionando um comando para fazer isso:rm -rf bar/* && cp -TRv foo/ bar/
theferrit32
1
Não sou um leitor de mentes ... Não vejo mais esclarecimentos sobre o que o OP estava procurando, mas esta era DEFINITIVAMENTE a resposta que eu (MEEEE) estava procurando
Assimilater
2
apenas no caso de alguém se enganar com o motivo do link do comentário mais votado não estar funcionando, aqui está: web.archive.org/web/20170909193852/https://developer.apple.com/…
Enxofre
48

Faça isso em duas etapas.

rm -r bar/
cp -r foo/ bar/
Jonathan Wheeler
fonte
11
Na verdade, este é o único exemplo dado até agora que garantirá que o barconteúdo seja idêntico a foo, e não uma combinação de itens de foomais outros itens que possam já existir em bar. A resposta altamente votada por @Saurabh Meshram abaixo tem esse problema.
robo
1
Tenha muito cuidado ao usar isso, pois irá remover todos os arquivos do bar, mesmo os mais ocultos.
Elia Grady
mac osx: rm -rf bar/; cp -r foo/ !$
Michael Dimmitt
1
Isso nem sempre é viável ... ou desejado ... imagine se fooe barforem grandes diretórios ... talvez haja arquivos barque não estão dentro e fooque você não deseja remover. Esta não deve ser considerada uma resposta realista imho
Assimilater
Embora funcionou para mim, mas não a melhor solução, uma vez que pode haver arquivo incluído em foo / mas não em bar / que será excluído após fazer isso.
Nitwit
46

Se você deseja garantir que bar/termina idêntico a foo/, use em seu rsynclugar:

rsync -a --delete foo/ bar/

Se apenas algumas coisas mudaram, isso será executado muito mais rápido do que remover e copiar todo o diretório.

  • -aé o 'modo de arquivo', que copia arquivos fielmente foo/parabar/
  • --deleteremove arquivos extras não na foo/de bar/bem, garantindo bar/acaba idênticos
  • Se você quiser ver o que está fazendo, adicione -vhpara verboso e legível
  • Nota: a barra após fooé necessária, caso contrário, rsyncserá copiado foo/em bar/foo/vez de sobrescrito bar/.
    • (As barras após os diretórios em rsync são confusas; se você estiver interessado, aqui está o furo. Eles dizem ao rsync para se referir ao conteúdo do diretório, em vez do próprio diretório . Portanto, para substituir o conteúdo de foo/no conteúdo debar/ , nós use uma barra em ambos. É confuso porque não funcionará como esperado com uma barra em nenhum ; rsync sorrateiramente sempre interpreta o caminho de destino como se tivesse uma barra, embora respeite a ausência de uma barra na origem caminho. Portanto, precisamos de uma barra no caminho de origem para que corresponda à barra adicionada automaticamente no caminho de destino, se quisermos copiar o conteúdo defoo/ para bar/, em vez do diretóriofoo/pousando em bar/como bar/foo.)

rsync é muito poderoso e útil, se você estiver curioso, procure o que mais ele pode fazer (como copiar sobre ssh).

Tom Potter
fonte
1
Gostei desta resposta melhor do que a cp -Topção porque também funciona em macosx 👍
tongueroo
18

Use este cpcomando:

cp -Rf foo/* bar/
anubhava
fonte
9
Isso não remove arquivos que estão presentes em bar, mas não em foo.
Ara
5
Não sei o que você quer dizer com isso. Por que o cpcomando deve remover o arquivo da fonte?
anubhava 01 de
Meu entendimento é que quando você 'sobrescreve um diretório existente' por outro, no final o diretório sobrescrito deve ser uma cópia do outro. Ou seja, na barra final deve haver uma cópia de foo, como é o caso de @jonathan-wheeler answer, mas se você tivesse um arquivo bar / ce nenhum foo / c, então bar / c não seria excluído. Em uma nota lateral, acabei de notar que também não é o caso da resposta de Saurabh Meshram.
Ara
Talvez não concordemos sobre o que significa sobrescrever, para mim é basicamente substituir, enquanto você pode significar megre? É difícil dizer o que OP deseja exatamente porque ela mencionou apenas 2 arquivos que estão em foo e bar.
Ara
Essa sintaxe também ignora arquivos de ponto ocultos. Um grande número também pode explodir o globo.
xpusostomos
13

O seguinte comando garante que dotfiles (arquivos ocultos) sejam incluídos na cópia:

$ cp -Rf foo/. bar
May Oakes
fonte
1
Isso é verdade? Parece incomum.
Mateng
2
@Mateng Acabei de testar - sim, é verdade.
Matmarbon
1
.significa todos os arquivos do diretório. Então, naturalmente, os arquivos ocultos seriam incluídos.
Jaspreet Singh
Isso é muito legal, na verdade. Você também pode fazer "cp -RTf foo bar" no Linux.
xpusostomos
5

Muito semelhante a @Jonathan Wheeler:

Se você não quer lembrar, mas não reescrever bar:

rm -r bar/
cp -r foo/ !$

!$ exibe o último argumento do comando anterior.

Michael Dimmitt
fonte
4
A dica sobre o! $ É incrível!
Lucas Morgan
0

isso deve resolver seu problema.

\cp -rf foo/* bar/
todos
fonte
-1

A operação que você definiu é uma "fusão" e você não pode fazer isso com ela cp. No entanto, se você não estiver procurando por mesclagem e barpuder perder a pasta , basta rm -rf barexcluir a pasta e mv foo barrenomeá-la. Isso não levará nenhum tempo, pois ambas as operações são feitas por ponteiros de arquivo, não pelo conteúdo do arquivo.

Ati
fonte
-2

Tente usar este comando composto de duas etapas:

rm -rf bar && cp -r foo bar
Yahor M
fonte