Mesclando pastas com o mv?


149

Se eu mvmover uma pasta chamada "pasta" para um diretório que já contenha "pasta", elas serão mescladas ou serão substituídas?

Respostas:


120

mvnão puder mesclar ou substituir diretórios, ele falhará com a mensagem "mv: não é possível mover 'a' para 'b': diretório não vazio" , mesmo quando você estiver usando a --forceopção


Você pode contornar isso usando outras ferramentas (como rsync, findou até mesmo cp), mas você precisa considerar com cuidado as implicações:

  • rsyncpode mesclar o conteúdo de um diretório em outro (idealmente com a opção --remove-source-files1 para excluir com segurança apenas os arquivos de origem que foram transferidos com êxito e com a opção usual de permissão / propriedade / preservação de tempo, -ase desejar)
    ... mas essa é uma operação de cópia completa e, portanto, pode consumir muito disco.
  • Opção atualmente preferida: você pode combinar rsynca --link-dest=DIRopção (para criar links físicos em vez de copiar o conteúdo do arquivo, sempre que possível) e --remove-source-filesobter uma semântica muito semelhante a uma regular mv.
    Para isso, é --link-destnecessário fornecer um caminho absoluto para o diretório de origem (ou um caminho relativo do destino à origem ).
    Mas isso é usado de --link-destmaneira não intencional (que pode ou não causar complicações), requer conhecer (ou determinar) o caminho absoluto para a fonte (como argumento --link-dest) e novamente deixa uma estrutura de diretório vazia para ser limpa como por 1 .
  • Você pode usarfind para recriar seqüencialmente a estrutura do diretório de origem no destino e, em seguida, mover individualmente os arquivos reais
    ... mas isso deve ser repetido através da fonte várias vezes e pode encontrar condições de corrida (novos diretórios sendo criados na fonte durante o processo de várias etapas) )
  • cppode criar links físicos (basta colocar, ponteiros adicionais para o mesmo arquivo existente), o que cria um resultado muito semelhante a uma fusão mv(e é muito eficiente em IO, pois apenas ponteiros são criados e nenhum dado real precisa ser copiado)
    mas isso novamente sofre de uma possível condição de corrida (novos arquivos na fonte são excluídos, mesmo que não tenham sido copiados na etapa anterior)

Qual dessas soluções alternativas (se houver) é apropriada dependerá muito do seu caso de uso específico.
Como sempre, pense antes de executar qualquer um desses comandos e faça backups.


1: Observe que rsync --remove-source-filesnão excluirá nenhum diretório, então você terá que fazer algo como find -depth -type d -empty -deletedepois para se livrar da árvore de diretórios de origem vazia.


Parece que você tentou apenas uma implementação do mv. Essa resposta seria melhor com uma verdade mais ampla. Linux, BSD e Unix "real", ou uma referência do POSIX ou SUS.
Warren Young

@WarrenYoung Você está certo, eu só tentei o mvimplementação usada pelo Debian - a ênfase estar em julgado , uma vez que a página de manual não menciona este comportamento ...
n.st

38
A desvantagem do rsync é que ele realmente copia os dados, em vez de apenas alterar o link físico, o que potencialmente requer muitos recursos se você estiver lidando com muitos dados.
Jonathan Mayer

7
@ Keith Observe que --deleteapenas exclui arquivos no diretório de destino que não existem no diretório de origem.
n.st

1
@JonathanMayer rsync como vários recursos relacionados ao hard link. Por exemplo, você pode apenas preservar os links físicos com a -Hfunção ou pode vincular os arquivos no destino usando --link-dest. Veja a página de manual antes de usá-los.
allo

87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/

Isso removerá os arquivos de origem como no comentário do n.st?
Dominique

3
Não, eu preferiria fazê-lo em duas etapas por razões de segurança. A fonte mesclada e removida é irreversível. Também é necessário o passo Additon na n.ª resposta (para remover diretórios).
fazie

3
--remove-source-filestem a vantagem de remover apenas os arquivos transferidos com êxito; portanto, você pode usar findpara remover diretórios vazios e ficará com tudo o que não foi transferido sem ter que verificar rsynca saída.
n.st

4
Mas na verdade não está se movendo - o impacto na velocidade é enorme, se houver arquivos grandes envolvidos.
21416 Alex

Mas você não pode executar puro movimento e mesclagem, na verdade.
fazie

64

Você pode usar a -lopção do comando cp , que cria links físicos de arquivos no mesmo sistema de arquivos em vez de cópias de dados completos. O comando a seguir copia a pasta source/folderpara uma pasta pai ( destination) que já contém um diretório com o nome folder.

cp -rl source/folder destination
rm -r source/folder

Você também pode usar o -P( --no-dereference- não desassociar links simbólicos) ou -a( --archive- preservar todos os metadados, também inclui a -Popção), dependendo de suas necessidades.


7
@rautamiekka: Presumo que você esteja perguntando o motivo do uso de links físicos. Se você não sabe o que são links físicos e por que deve usá-los, provavelmente não deve seguir esse caminho. No entanto, a criação de links físicos não faz uma cópia completa; portanto, essa operação levaria ordens de magnitude menos tempo que uma cópia completa. E você usaria links físicos em vez de links físicos para poder excluir os arquivos de origem e ainda ter os dados corretos em vez de ponteiros para caminhos inválidos. E cpao invés de rsyncjá que todo sistema tem cpe todos têm familiaridade com ele.
palswim

7
O brilho desta solução pode ser ignorado por não ser a resposta aceita. É uma solução elegante. Você obtém a capacidade de mesclar cpcom o tempo de operação de mv.
TheHerk

2
se você sabe que você não precisa para mover arquivos que já existem no destino você também deseja adicionar-n
ndemou

1
@Ruslan: Isso é verdade, mas você não pode se mover sem uma cópia nos sistemas de arquivos com qualquer método. Even mv /fs1/file /fs2/(entre sistemas de arquivos) executará uma cópia e, em seguida, uma exclusão.
palswim

2
Certo, mas while mvfuncionará (desde que o diretório de destino ainda não exista), mesmo que não seja "eficiente" ou como você o chamar, cp -rlfalhará.
Ruslan

23

Eu recomendo estas quatro etapas:

cd ${SOURCE}; 
find . -type d -exec mkdir -p ${DEST}/\{} \; 
find . -type f -exec mv \{} ${DEST}/\{} \; 
find . -type d -empty -delete

ou melhor ainda, aqui está um script que implementa semântica semelhante a mv:

#!/bin/bash

DEST="${@:${#@}}"
ABS_DEST="$(cd "$(dirname "$DEST")"; pwd)/$(basename "$DEST")"

for SRC in ${@:1:$((${#@} -1))}; do   (
    cd "$SRC";
    find . -type d -exec mkdir -p "${ABS_DEST}"/\{} \;
    find . -type f -exec mv \{} "${ABS_DEST}"/\{} \;
    find . -type d -empty -delete
) done

Args são SOURCE, DEST
schuess

Isso parece bastante útil. Estou tentado a usá-lo para limpar meu disco rígido. Outros especialistas podem comentar sobre isso antes de eu confiar vários backups nesse script? :-)
LarsH

BTW, se você deseja fazer o equivalente a rsync -u(somente atualizar se for mais recente), mv(em algumas versões pelo menos) também pode usar a -uopção. No entanto, nesse caso, convém excluir os diretórios de origem não vazios e os vazios, para cobrir os casos em que os arquivos na árvore de origem não são mais novos. @chuess: Parece que pode haver vários argumentos SOURCE, se você precisar.
Larsh

1
Isso não lida bem com espaços em branco. Eu tentei com alguns diretórios com espaços neles e acabei com uma série infinita de diretórios aninhados.
Rofer

16

Aqui está uma maneira de mesclar os diretórios. É muito mais rápido que o rsync, pois apenas renomeia os arquivos em vez de copiá-los e excluí-los.

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'

Isso é interessante, mas apenas vagamente relevante para o tópico e nem remotamente o que o usuário perguntou.
Shadur 5/09/14

17
Na verdade, o código de Jewel faz exatamente o que o usuário solicitou, com exceção da criação de diretórios ausentes. Talvez você deva procurar de novo?
Jonathan Mayer

3
Eu adicionaria para usar "-print0" em find e "-0" em xargs porque existem arquivos que possuem espaços nos nomes. Além disso, há um pequeno problema, se um nome contiver parênteses, eles não serão movidos.
Markuz

2
Isso é muito mais rápido que o rsync para um pequeno número de arquivos, mas cria um novo processo para cada arquivo, portanto, o desempenho é péssimo para um grande número de arquivos pequenos. A resposta de @ palswim não sofre com esse problema.
b0fh

2
O comando falhará, se in destjá for um diretório com o mesmo nome que em source. E os arquivos serão movidos para um dest, que está em source. O comando não faz nada mais do quemv source/* source/dest/.
ceving

3

Uma maneira de conseguir isso seria usar:

mv folder/* directory/folder/
rmdir folder

Contanto que não haja dois arquivos com o mesmo nome foldere directory/folder, você obterá o mesmo resultado, ou seja, mesclando.


3
Como exatamente rm folderfunciona?
JakeGould #

5
@JakeGould De maneira alguma. :)
n.st

rm folder -fRsempre funciona para mim
Octopus

2
Esteja ciente de que isso não vai funcionar para os arquivos ocultos
b0fh

2

Para as cópias mais puras, eu uso o método de cópia em bloco (tar) tar (-) B.

Por exemplo, no caminho de origem ('cd', se necessário):

tar cBf - <sourcefolder> | (cd /your/target/folder ; tar xBf -)

isso cria uma cópia exata da árvore de origem, COM o proprietário e as permissões intactas. E se a pasta de destino existir, os dados serão mesclados. Somente arquivos que já existem serão substituídos.

Exemplo:

 $ cd /data1/home
 $ tar cBf - jdoe | (cd /data2/home ; tar xBf -)

Quando a ação de cópia é bem-sucedida, você pode remover a fonte ( rm -rf <source>). Obviamente, essa não é uma ação exata: os dados serão copiados até você remover a fonte.

Como opção, você pode ser detalhado (exibir na tela o arquivo que está sendo copiado), com -v: tar cBvf -

  • c: crio
  • B: leia bloco completo (para leitura de tubo)
  • v: verbose
  • f: arquivo a ser gravado
  • x: extrair
  • -: stdout / stdin

sourcefoldertambém pode ser *(para qualquer coisa na pasta atual)


A especificação f -do tar geralmente não é necessária - o padrão é ler de stdin / write para stdout.
Muru

1

Aqui está um script que funcionou para mim. Eu prefiro mv sobre rsync, então uso as soluções de Jewel e Jonathan Mayer.

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd $SRC;
    find . -type d -exec mkdir -p ${DEST}/{} \;
    find . -type f -exec mv {} ${DEST}/{} \;
    find . -type d -empty -delete
    popd
done

Esta solução não escapa corretamente os nomes dos caminhos, tenha cuidado.
user12439

@ user12439, atualizarei a solução se você me mostrar qual parte corrigir.
Xer0x

1

Não é uma boa ideia usar comandos como cp ou rsync. Para arquivos grandes, levará muito tempo. O mv é muito mais rápido, pois somente atualiza os inodes sem copiar os arquivos fisicamente. Uma opção melhor é usar o gerenciador de arquivos do seu sistema operacional. Para o Opensuse, existe um gerenciador de arquivos chamado Konquerer. Ele pode mover arquivos sem realmente copiá-los. Possui a função "recortar e colar", como no Windows. Basta selecionar todos os subdiretórios no diretório A. Clique com o botão direito e "vá para" o diretório B, que pode conter subdiretórios com os mesmos nomes. Ele irá fundi-los. Também existem opções para substituir ou renomear arquivos com o mesmo nome.


1
O OP pergunta o que acontece quando mvé usado.
don_crissti

0

Solução Python

Como não consegui encontrar uma solução pré-existente satisfatória, decidi criar um script Python rápido para alcançá-la.

Em particular, esse método é eficiente porque apenas percorre a árvore do arquivo de origem uma vez que estiver de baixo para cima.

Ele também permitirá que você ajuste rapidamente coisas como manipulação de substituição de arquivos ao seu gosto.

Uso:

move-merge-dirs src/ dest/

moverá todo o conteúdo de src/*para dest/e src/desaparecerá.

mover-mesclar-dirs

#!/usr/bin/env python3

import argparse
import os

def move_merge_dirs(source_root, dest_root):
    for path, dirs, files in os.walk(source_root, topdown=False):
        dest_dir = os.path.join(
            dest_root,
            os.path.relpath(path, source_root)
        )
        if not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        for filename in files:
            os.rename(
                os.path.join(path, filename),
                os.path.join(dest_dir, filename)
            )
        for dirname in dirs:
            os.rmdir(os.path.join(path, dirname))
    os.rmdir(source_root)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Move merge src/* into dest. Overwrite existing files.'
    )
    parser.add_argument('src_dir')
    parser.add_argument('dest_dir')
    args = parser.parse_args()
    move_merge_dirs(args.src_dir, args.dest_dir)

GitHub upstream .

Consulte também: https://stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command


0

Este é o comando para mover arquivos e pastas para outro destino:

$ mv /source/path/folder /target/destination/

Lembre - se : o mvcomando não funcionará se a pasta estiver aberta (ou seja, já existe outra pasta com o mesmo nome no destino) e o diretório não está ativado .

mv: não é possível mover '/ source / path / folder' para '/ target / destination / folder': diretório não vazio

Se a pasta de destino estiver vazia, o comando acima funcionará bem.

Portanto, para mesclar as duas pastas em qualquer caso,
faça-o em 2 comandos:

$ cp -rf /source/path/folder /target/destination/
$ rm -rf /source/path/folder

Ou combine ambos como um comando único:

$ cp -rf /source/path/folder /target/destination/ && rm -rf /source/path/folder

mv = mover
cp = copiar
rm = remover

-r para diretório (pasta)
-f força a execução

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.