Como copiar um diretório recursivamente usando hardlinks para cada arquivo

52

Eu quero criar uma "cópia" de uma árvore de diretórios onde cada arquivo é um link direto para o arquivo original

Exemplo: Eu tenho uma estrutura de diretórios:

dirA/
dirA/file1
dirA/x/
dirA/x/file2
dirA/y/
dirA/y/file3

Aqui está o resultado esperado, uma "cópia" da árvore de diretórios em que cada arquivo é um link direto para o arquivo original:

dirB/            #  normal directory
dirB/file1       #  hardlink to dirA/file1
dirB/x/          #  normal directory
dirB/x/file2     #  hardlink to dirA/x/file2
dirB/y/          #  normal directory
dirB/y/file3     #  hardlink to dirA/y/file3
Gudmundur Orn
fonte

Respostas:

50

No Linux (mais precisamente com o GNU e busyboximplementações cpnormalmente encontradas em sistemas que têm Linux como kernel) e no FreeBSD recente, é assim:

cp -al dirA dirB

Para uma solução mais portátil, consulte a resposta usando pax e cpio por Stéphane Chazelas

Gudmundur Orn
fonte
Observe que pax, como no FreeBSD, cp -anão vincula links simbólicos.
Stéphane Chazelas
Esteja ciente de que links físicos não funcionam em montagens de sistema de arquivos separadas.
Dave
24

POSIX, você usaria paxno modo de leitura + gravação com a -lopção:

pax -rwlpe -s /A/B/ dirA .

( -pePreserva todos os atributos possíveis de arquivos (neste caso apenas diretórios) que são copiados, como GNU cp's -afaz).

Agora, embora padrão , esse comando não é necessariamente muito portátil .

Primeiro, muitos sistemas baseados em GNU / Linux não os incluem paxpor padrão (mesmo que seja um utilitário POSIX não opcional).

Em seguida, vários bugs e não conformidades com algumas implementações causam vários problemas com esse código.

  • por causa de um erro, o Solaris 10 pax(pelo menos) não funciona quando usado -rwlem combinação com -s. Por alguma razão, parece que aplica a substituição ao caminho original e copiado. Então, acima, ele tentaria fazer alguns em link("dirB/file", "dirB/file")vez de link("dirA/file", "dirB/file").
  • no FreeBSD, paxnão cria hardlinks para arquivos do tipo link simbólico (um comportamento permitido pelo POSIX). Não apenas isso, mas também aplica a substituição aos destinos dos links simbólicos (um comportamento não permitido pelo POSIX). Assim, por exemplo, se há um foo -> AAlink simbólico em dirA, ele se tornará foo -> BAno dirB.

Além disso, se você quiser fazer o mesmo, mas com caminhos de arquivo arbitrários cujo conteúdo é armazenado $srce $dst, é importante perceber que pax -rwl -- "$src" "$dst"cria toda a estrutura de diretórios do $srcinterior $dst(que precisa existir e ser um diretório). Por exemplo, se $srcfor foo/bar, então, $dst/foo/baré criado.

Se, em vez disso, você deseja $dstser uma cópia $src, o mais fácil é provavelmente fazê-lo como:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && pax -rwlpe . "$absolute_dst")

(que também solucionaria a maioria dos problemas mencionados acima, mas falharia se o caminho absoluto de $dstterminasse em caracteres de nova linha).

Agora isso não ajuda em sistemas GNU / Linux onde não há pax.

É interessante notar que paxfoi criado pelo POSIX para mesclar os recursos dos comandos tare cpio.

cpioé um comando histórico do Unix (de 1977) em oposição a uma invenção POSIX, e também existe uma implementação do GNU (não uma pax). Portanto, mesmo que não seja mais um comando padrão (era no SUSv2), ainda é muito comum, e há um conjunto principal de recursos nos quais você geralmente pode confiar.

O equivalente a pax -rwlseria cpio -pl. Contudo:

  1. cpio pega a lista de arquivos de entrada no stdin em vez de argumentos (delimitado por nova linha, o que significa que os nomes de arquivo com caracteres de nova linha não são suportados)
  2. Todos os arquivos precisam ser especificados (normalmente você fornece a saída de find( finde cpioforam desenvolvidos em conjunto pelas mesmas pessoas)).
  3. os metadados não são preservados (algumas cpioimplementações têm opções para preservar alguns, mas nada portátil).

Então com cpio:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && find . | cpio -pl "$absolute_dst")
Stéphane Chazelas
fonte
Parece que -s / A / B / é específico para o meu exemplo. Como você faria isso se o nome do diretório de origem e o nome do diretório de destino fossem variáveis ​​$ sourcedir e $ targetdir?
Gudmundur Orn
@GudmundurOrn, veja editar.
Stéphane Chazelas
Eu executo este comando no OS X e recebo apenas uma mensagem de erro "pax: Não é possível vincular o arquivo ./a.txt a ele próprio". Eu usei o seu comando literalmente, apenas substituindo o diretório de origem pelo nome real, deixando / A / B e o ponto final como está. Estou entendendo mal alguma coisa?
db
@db, -s /A/Bsubstitui Apor Bpara que dirAse torne dirB. Se o nome do diretório de origem não tiver A, então ele será copiado (link) sobre ele mesmo. Veja também o restante da resposta para abordagens possivelmente melhores.
Stéphane Chazelas
6

Resposta curta:

cd $source_folder
pax -rwlpe . $dest_folder
lkraider
fonte
2

Caso você esteja procurando esse recurso de cópia com hardlinks para fazer snapshots ou backups (total ou parcial) de seus arquivos, consulte rsnapshot.

Janis
fonte
11
Isso é interessante. Mas acho que os links físicos são apenas um bom mecanismo de captura instantânea se os arquivos não forem modificados. Direito?
Gudmundur Orn
@Gudmundur Orn; Isto está correto. A ferramenta mencionada na minha resposta criará um novo instantâneo de maneira que os arquivos sejam únicos; ou seja, arquivos existentes (não modificados) serão criados como links físicos e novos arquivos (ou versões modificadas de arquivos existentes) serão criados como novos arquivos. Portanto, em conseqüência, você terá a menor redundância.
Janis
0

A resposta de @ gudmundur-orn está correta, mas se você estiver no BtrFS no Linux, cp a --reflink=auto dirA dirBdeve fazer o truque, com a diferença de que os arquivos são realmente diferentes e mudar um não muda o outro. Você pode conseguir o mesmo com cp -cum Mac com APFS ( autofará uma cópia completa, se não for possível, -cfalhará).

Qualquer sistema de arquivos COW deve ser capaz de fazer isso, mas os fornecedores não concordaram com uma opção de linha de comando padrão.

rbanffy
fonte