Come copiare ricorsivamente una directory usando hardlink per ogni file


52

Voglio creare una "copia" di un albero di directory in cui ogni file è un collegamento reale al file originale

Esempio: ho una struttura di directory:

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

Ecco il risultato atteso, una "copia" della struttura di directory in cui ogni file è un collegamento reale al file originale:

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

Risposte:


50

Su Linux (più precisamente con la GNU e le busyboximplementazioni cpcome normalmente si trovano sui sistemi che hanno Linux come kernel) e FreeBSD recente, ecco come:

cp -al dirA dirB

Per una soluzione più portatile, vedi la risposta usando pax e cpio di Stéphane Chazelas


Nota che ad esempio paxsu FreeBSD cp -anon sono presenti collegamenti simbolici.
Stéphane Chazelas,

Tenere presente che i collegamenti fisici non funzionano su supporti separati per filesystem.
Dave,

24

POSIXly, useresti paxin modalità read + write con l' -lopzione:

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

( -peConserva tutti i possibili attributi di file (in questo caso solo directory) che vengono copiati, come GNU cp's -afa).

Ora, sebbene standard , quel comando non è necessariamente molto portatile .

Innanzitutto, molti sistemi basati su GNU / Linux non includono paxper impostazione predefinita (anche se si tratta di un'utilità POSIX non opzionale).

Quindi, una serie di bug e non conformità con alcune implementazioni causano una serie di problemi con quel codice.

  • a causa di un bug, Solaris 10 pax(almeno) non funziona se utilizzato -rwlin combinazione con -s. Per qualche motivo, sembra che applichi la sostituzione sia al percorso originale che a quello copiato. Quindi sopra, tenterebbe di fare un po ' link("dirB/file", "dirB/file")invece di link("dirA/file", "dirB/file").
  • su FreeBSD, paxnon crea hardlink per file di tipo symlink (un comportamento consentito da POSIX). Non solo, ma applica anche la sostituzione alle destinazioni dei collegamenti simbolici (un comportamento non consentito da POSIX). Così, per esempio, se c'è un foo -> AAlink simbolico a dirA, diventerà foo -> BAin dirB.

Inoltre, se vuoi fare lo stesso ma con percorsi di file arbitrari il cui contenuto è archiviato $srce $dst, è importante rendersi conto che pax -rwl -- "$src" "$dst"crea l'intera struttura di directory di $srcinside $dst(che deve esistere ed essere una directory). Ad esempio, se $srcè foo/bar, quindi, $dst/foo/barviene creato.

Se invece vuoi $dstessere una copia di $src, il più semplice è probabilmente farlo come:

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

(che aggirerebbe anche la maggior parte dei problemi sopra menzionati ma fallirebbe se il percorso assoluto $dsttermina in caratteri di nuova riga).

Ora questo non sarà di aiuto sui sistemi GNU / Linux dove non c'è pax.

È interessante notare che è paxstato creato da POSIX per unire le funzionalità dei comandi tare cpio.

cpioè un comando Unix storico (dal 1977) in contrapposizione a un'invenzione POSIX, e c'è anche un'implementazione GNU (non una pax). Quindi, anche se non è più un comando standard (era in SUSv2), è comunque molto comune e c'è un set base di funzionalità su cui di solito puoi fare affidamento.

L'equivalente di pax -rwlsarebbe cpio -pl. Però:

  1. cpio accetta l'elenco di file di input su stdin anziché su argomenti (delimitato da nuova riga, il che significa che i nomi di file con caratteri di nuova riga non sono supportati)
  2. Tutti i file devono essere specificati (in genere si alimenta l'output di find( finde cpiosono stati sviluppati congiuntamente dalle stesse persone)).
  3. i metadati non vengono conservati (alcune cpioimplementazioni hanno opzioni per preservarne alcune, ma nulla di portatile).

Quindi con cpio:

absolute_dst=$(umask 077 && mkdir -p -- "$dst" && cd -P -- "$dst" && pwd -P) &&
(cd -P -- "$src" && find . | cpio -pl "$absolute_dst")

Sembra che -s / A / B / sia specifico del mio esempio. Come faresti se il nome della directory di origine e il nome della directory di destinazione fossero variabili $ sourcedir e $ targetdir?
Gudmundur Orn,

@GudmundurOrn, vedi modifica.
Stéphane Chazelas,

Eseguo questo comando su OS X e ricevo solo un messaggio di errore "pax: impossibile collegare il file ./a.txt a se stesso". Ho usato letteralmente il tuo comando, semplicemente sostituendo la directory dei sorgenti con il nome attuale, lasciando / A / B e il punto finale così com'è. Sto fraintendendo qualcosa?
db

@db, -s /A/Bsostituisce Acon Bcosì dirAdiventa dirB. Se il nome della directory di origine non ha A, il file verrà copiato (collegato) su se stesso. Vedi anche il resto della risposta per approcci forse migliori.
Stéphane Chazelas,

6

Risposta breve:

cd $source_folder
pax -rwlpe . $dest_folder

2

Nel caso in cui tu stia cercando quella funzione copia-con-hardlink per fare delle istantanee o dei backup di (tutti o parte dei) i tuoi file, dai un'occhiata rsnapshot.


1
Interessante. Ma immagino che i collegamenti fisici siano solo un buon meccanismo di istantanea se i file non verranno modificati. Giusto?
Gudmundur Orn,

@Gudmundur Orn; Questo è corretto. Lo strumento menzionato nella mia risposta creerà una nuova istantanea in modo che i file siano unici; ovvero i file esistenti (non modificati) verranno creati come collegamenti fisici e i nuovi file (o versioni modificate dei file esistenti) verranno creati come nuovi file. Quindi di conseguenza avrai la ridondanza minima.
Janis,

0

La risposta di @ gudmundur-orn è corretta, ma se sei su BtrFS su Linux cp a --reflink=auto dirA dirBdovresti fare il trucco, con la differenza che i file sono effettivamente diversi e cambiare l'uno non cambia l'altro. Puoi ottenere lo stesso risultato con cp -cun Mac con APFS ( autose non è possibile eseguirne una copia completa, non -criuscirà).

Qualsiasi file system COW dovrebbe essere in grado di farlo, ma i fornitori non hanno concordato un'opzione della riga di comando standard.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.