Bash: copia i file nominati in modo ricorsivo, preservando la struttura delle cartelle


103

Io stavo sperando:

cp -R src/prog.js images/icon.jpg /tmp/package

produrrebbe una struttura simmetrica nella directory di destinazione:

/tmp
|
+-- package
    |
    +-- src
    |   |
    |   +-- prog.js
    |
    +-- images
        |
        +-- icon.jpg

ma invece, entrambi i file vengono copiati in / tmp / package. Una copia piatta. (Questo è su OSX).

Esiste una semplice funzione bash che posso usare per copiare tutti i file, inclusi i file specificati da caratteri jolly (ad esempio src / *. Js) nella loro giusta posizione all'interno della directory di destinazione. Un po 'come "per ogni file, esegui mkdir -p $(dirname "$file"); cp "$file" $(dirname "$file")", ma forse un singolo comando.

Questo è un thread rilevante, il che suggerisce che non è possibile. La soluzione dell'autore non mi è però così utile, perché vorrei semplicemente fornire un elenco di file, caratteri jolly o meno, e copiarli tutti nella directory di destinazione. IIRC MS-DOS xcopy lo fa, ma sembra che non ci sia equivalente per cp.

Risposte:


154

Hai provato a utilizzare l'opzione --parents? Non so se OS X lo supporta, ma funziona su Linux.

cp --parents src/prog.js images/icon.jpg /tmp/package

Se questo non funziona su OS X, prova

rsync -R src/prog.js images/icon.jpg /tmp/package

come suggerito da aif.


4
Grazie. risulta che "cp --parents" non è possibile su Mac, ma è bello conoscere il flag per altri unixen. rsync -R è la soluzione portatile più semplice per questo problema.
mahemoff

1
Ho accettato questo per la sua eleganza / memorabilità, ma ho appena scoperto che non copia intere directory (almeno su OSX), mentre quello tar qui sotto lo fa.
mahemoff

cp --parentsè un'opzione illegale in OSX (BSD cp), ma gcp(GNU cp) funziona bene. Se non è ancora nel tuo sistema, usa brew install coreutils. U avrà molte utility con g-prefix.
kyb

@mahemoff cp -R --parentse rsync -rRcopia sia i file che le directory relativamente.
Vortico

22

Senso unico:

tar cf - <files> | (cd /dest; tar xf -)

oh, mi piace molto di più della mia risposta.
EMPraptor

4
Puoi anche usare l' -Copzione per fare il chdir per te - tar cf - _files_ | tar -C /dest xf -o qualcosa del genere.
D.Shawley

grazie, questo è conciso, anche se preferisco rsync per semplicità.
mahemoff

1
Grande! Qualcuno sa come trasformarlo in un comando di shell? Dovrebbe accettare N input, i primi N-1 sono i file da copiare e l'ultimo è la cartella di destinazione.
arrivo il

@arod $ {! #} è l'ultimo parametro e usalo per ottenere gli argomenti precedenti stackoverflow.com/questions/1215538/… . Se scrivi il comando, inserisci qui il link all'essenziale.
mahemoff

18

In alternativa, se sei vecchia scuola, usa cpio:

cd /source;
find . -print | cpio -pvdmB /target

Chiaramente, puoi filtrare l'elenco dei file a tuo piacimento.

L'opzione "-p" è per la modalità "pass-through" (come contro "-i" per l'input o "-o" per l'output). "-V" è dettagliato (elenca i file mentre vengono elaborati). La "-m" conserva i tempi di modifica. "-B" significa utilizzare "blocchi grandi" (dove i blocchi grandi sono 5120 byte invece di 512 byte); è possibile che non abbia alcun effetto in questi giorni.


1
È meglio usare -print0con la combinazione di --nullopzioni in modo che non si rompa con caratteri speciali e simili:find . -print0 | cpio -pvdmB --null /target
haridsv

Chiamalo vecchia scuola, chiamalo portatile, chiamalo come vuoi, ma mi fido decisamente cpiodi questo compito. Sono d'accordo che le opzioni -print0e -nulldovrebbero essere usate, altrimenti, a un certo punto, qualcuno ti darà alcune cartelle con "caratteri speciali" (spazi, molto probabilmente) e succederà qualcosa. Non che io parli per esperienza personale, ma potresti provare a eseguire il backup di un mucchio di file e finire con il backup solo della metà a causa degli spazi nei nomi dei file. (Ok, parlo per esperienza personale.)
bballdave025

@ bballdave025: si verificano problemi solo con cpiocome mostrato se i nomi dei file contengono una nuova riga - quindi normalmente si finisce con un messaggio di errore su due (o più) nomi di file non trovati per ogni nuova riga in un nome di file. (A volte, potresti ricevere meno messaggi, ma ciò richiede una notevole cura nella costruzione del test case.). Quando l'ho usato cpio, non c'era --nullopzione; le opzioni a doppio trattino non facevano parte delle notazioni delle opzioni SVR4 e il concetto di -print0non era presente in findnessuno dei due. Ma è molto tempo fa (metà degli anni '90 per esempio, prima che Linux raggiungesse il dominio).
Jonathan Leffler

Grazie per i dettagli, @Jonathan_Leffler. Adoro imparare cose qui. Ora, sto cercando di ricordare quale errore di copia ricorsiva ho fatto che mi ha dato problemi con gli spazi - ci sono molti modi in cui avrei potuto commettere quell'errore,
bballdave025

@ bballdave025— Una possibilità è usare xargsnel mix - si divide in spazi bianchi - spazi vuoti, tabulazioni, ritorno a capo. OTOH, non sono sicuro di come o perché lo faresti. Il cpiomanuale GNU sulla modalità di output è chiaro su un nome di file per riga. Il manuale utente di SVR4 (stampato nel 1990) è più vago: cpio -o(modalità copy-out) legge lo standard input per ottenere un elenco di nomi di percorso e copia quei file sullo standard output insieme al nome del percorso e alle informazioni sullo stato. C'è una possibilità, quindi, che abbia rotto i nomi negli spazi.
Jonathan Leffler

16

L'opzione -R di rsync farà quello che ti aspetti. È una copiatrice di file molto ricca di funzionalità. Per esempio:

$ rsync -Rv src/prog.js images/icon.jpg /tmp/package/
images/
images/icon.jpg
src/
src/prog.js

sent 197 bytes  received 76 bytes  546.00 bytes/sec
total size is 0  speedup is 0.00

Risultati del campione:

$ find /tmp/package
/tmp/package
/tmp/package/images
/tmp/package/images/icon.jpg
/tmp/package/src
/tmp/package/src/prog.js


1

Provare...

for f in src/*.js; do cp $f /tmp/package/$f; done

quindi per quello che stavi facendo originariamente ...

for f in `echo "src/prog.js images/icon.jpg"`; do cp $f /tmp/package/$f; done

o

v="src/prog.js images/icon.jpg"; for f in $v; do cp $f /tmp/package/$f; done

Non hai bisogno echoo $vqui. Anche questo metodo fallirà se la directory corrispondente non esiste nella destinazione.
Weijun Zhou
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.