Come copiare una cartella in modo ricorsivo in modo idempotente usando cp?
Su Dicembre 1, 2020 da adminQuando utilizzo
cp -R inputFolder outputFolder
il risultato è dipendente dal contesto :
- se
outputFolder
non esiste, sarà essere creato e il percorso della cartella clonata saràoutputFolder
. - se
outputFolder
esiste, il clone creato sarà essereoutputFolder/inputFolder
È orribile , perché voglio creare uno script di installazione e se lutente lo esegue due volte per errore, avrà outputFolder
creato la prima volta, poi alla seconda esecuzione tutto il materiale verrà essere creato ancora una volta in outputFolder/inputFolder
.
- Voglio sempre il primo comportamento: creare un clone accanto alloriginale (come fratello).
- Voglio utilizzare
cp
per essere portatile (ad es. MINGW norsync
spedito) - Ho controllato
cp -R --parents
ma questo ricrea il percorso fino allalbero delle directory (quindi il clone non saràoutputFolder
masome/path/outputFolder
) -
--remove-destination
o--update
nel caso 2 non cambi nulla, le cose vengono comunque copiate inoutputFolder/inputFolder
È cè un modo per farlo senza prima verificare lesistenza di outputFolder
(se la cartella non esiste allora …) o utilizzando rm -rf outputFolder
?
Qual è il modo UNIX portabile e concordato per farlo?
Commenti
Risposta
Utilizza invece questo:
cp -R inputFolder/. outputFolder
Funziona esattamente nello stesso modo in cui, ad esempio, cp -R aaa/bbb ccc
funziona: se ccc
non “t esiste allora” sono creati come una copia di bbb
e dei suoi contenuti; ma se ccc
esiste già, ccc/bbb
viene creato come copia di bbb
e dei suoi contenuti .
Per quasi tutte le istanze di bbb
questo dà il comportamento indesiderabile che hai notato nella tua Domanda. Tuttavia, in questa situazione specifica, bbb
è solo .
, quindi aaa/bbb
è in realtà aaa/.
, che a sua volta in realtà è solo aaa
ma con un altro nome. Quindi abbiamo questi due scenari:
-
ccc
non esiste:Il comando
cp -R aaa/. ccc
significa” creaccc
e copia i contenuti diaaa/.
inccc/.
, ovvero copiaaaa
inccc
. -
ccc
esiste:Il comando
cp -R aaa/. ccc
significa” copia il contenuto diaaa/.
inccc/.
, ovvero copiaaaa
inccc
.
Commenti
- Eh, questo ‘ è nuovo per me, ma sembra funzionare! È documentato ovunque?
- ‘ ho testato
inputFolder/.
invece diinputFolder
e in effetti funziona per me su Windows / Git Bash. (Tuttavia non funziona solo con un/
finale, ho bisogno anche del punto dopo la barra).Ho dato +1 poiché ‘ è interessante, anche se ‘ è probabilmente un po troppo complicato per usarlo nel codice pubblico 🙂 - @IlmariJaronen ‘ non ha bisogno di essere documentato esplicitamente. Segui la spiegazione e ‘ vedrai semplicemente come ” segue le regole ” .
Risposta
Non copiare la cartella, copia solo il contenuto:
## Create the target directory. The -p suppresses error messages ## if the directory already exists mkdir -p outputFolder ## Copy the contents recursively, this will not recreate the parent cp -R inputfolder/* outputfolder/
In questo modo assicurate entrambi che la directory di destinazione venga creata la prima volta che lo script viene eseguito ed evitate il problema quando viene eseguito una seconda volta.
Chris Down sottolinea molto correttamente che in bash, questo salterà i file il cui nome inizia con un .
. Per evitare questo , puoi eseguire shopt -s dotglob
prima di eseguire il comando precedente.
Entrambi -p
per mkdir
e -R
per cp
sono definiti da POSIX quindi dovrebbe essere perfettamente portabile.
Commenti
- Nota. In questo modo ‘ non copia i file che iniziano con
.
. In bash, puoi usaredotglob
per questo. - Nota che questo metodo probabilmente fallirà se il numero di voci di directory nella cartella di input è grande, perché lespansione glob potrebbe superare la lunghezza massima della riga di comando.
Risposta
Prova -T
opzione per cp
. Questo esiste in GNU coreutils cp
versione 8.22; potrebbe non essere portabile al di fuori di questo.
Commenti
- Sì, sembra che questo sia esattamente quello che volevo:
cp -T
(cp --no-target-directory
). unix.stackexchange.com/questions/94831/… Purtroppo la mia versione dicp
è molto più vecchio. - +1 Utilizzato solo oggi.
-u
è unottima opzione da aggiungere per renderlo pigro.
Rispondi
Puoi anche utilizzare rsync
:
rsync -uav inputFolder/ outputFolder/
(nota le barre, specialmente dopo la prima )
Risposta
Puoi utilizzare lopzione -t
di cp
comando come:
cp -R inputFolder -t outputFolder
ora se la cartella di destinazione non esiste, verrà generato un errore:
cp: failed to access ‘outputFolder’: No such file or directory
nota sopra il comando copierà inputFolder
insieme al suo contenuto (e non solo al contenuto al suo interno)
se vuoi copiare solo il contenuto di inputFolder
diventa un po complicato (dato che devi stare attento quando usi il globbing della shell mentre usi lasterisco *)
cp -R -t outputFolder/ -- inputFolder/*
ora se la cartella di destinazione non esiste, verrà generato un errore:
cp: failed to access ‘outputFolder’: No such file or directory
funziona con cp (GNU coreutils) 8.23
Risposta
Secondo me, non cè niente di più semplice e portabile di rm -rf outputFolder
. Quindi, continuo a farlo. Capisco che la tua domanda sia diversa, ma penso che questa sia la best practice.
Commenti
- Non riesce se ci fossero autorizzazioni o proprietà specifiche sul target che doveva essere mantenuto. O se fosse un collegamento simbolico.
- La domanda vuole che il risultato di
cp
sia lo stesso, sia quando la directory non esiste sia quando esiste. Allora, di cosa stai parlando? - Il comando
cp
dato dallOP non ‘ mantiene le autorizzazioni (o timestamp).
mv
di spostare una raccolta di file in una singola normale?rsync
?cp
, il collegamento tra due comanditar
è un modo piacevole e affidabile per copiare alberi.rsync
spedito.tar -C input -cf - . | tar -C output -xf -
funziona.-C
cambia la directory di lavoro, ma non è unopzione standard, quindi se ‘ non è supportata, devi eseguire i due tars in le giuste directory di lavoro per cominciare, ad es( cd input && tar -cf - . ) | ( cd output && tar -xf - )
Tuttavia, se ‘ hai a che fare con Windows, userei semplicemente roaima ‘ s answer .