Hvordan kopierer jeg en mappe rekursivt på en idempotent måte ved hjelp av cp?
On desember 1, 2020 by adminNår jeg bruker
cp -R inputFolder outputFolder
blir resultatet kontekstavhengig :
- hvis
outputFolder
ikke eksisterer, vil den blir opprettet, og den klonede mappestien vil væreoutputFolder
. - Hvis
outputFolder
eksisterer, vil den opprettet klonen være væroutputFolder/inputFolder
Dette er fryktelig fordi jeg vil lage et installasjonsskript, og hvis brukeren kjører det to ganger ved en feiltakelse, vil han ha outputFolder
opprettet første gang, så vil andre ting på den andre kjøringen opprettes igjen i outputFolder/inputFolder
.
- Jeg vil alltid ha den første oppførselen: lag en klon ved siden av originalen (som søsken).
- Jeg vil bruke
cp
for å være bærbar (f.eks. MINGW gjør ikke det harrsync
sendt) - Jeg sjekket
cp -R --parents
men dette gjenskaper banen helt opp i katalogtreet (så klonen vil ikke væreoutputFolder
mensome/path/outputFolder
) -
--remove-destination
eller--update
i tilfelle 2 ikke endrer noe, fremdeles blir ting kopiert tiloutputFolder/inputFolder
Er det er en måte å gjøre dette uten å først sjekke om outputFolder
(hvis mappen ikke eksisterer da …) eller bruke rm -rf outputFolder
?
Hva er den avtalte, bærbare UNIX-måten å gjøre det på?
Kommentarer
Svar
Bruk dette i stedet:
cp -R inputFolder/. outputFolder
Dette fungerer på nøyaktig samme måte som for eksempel cp -R aaa/bbb ccc
fungerer: hvis ccc
ikke eksisterer, så eksisterer det ikke s opprettet som en kopi av bbb
og innholdet; men hvis ccc
allerede eksisterer, blir ccc/bbb
opprettet som kopien av bbb
.
For nesten alle tilfeller av bbb
gir dette uønsket oppførsel som du bemerket i spørsmålet ditt. I denne spesifikke situasjonen er bbb
bare .
, så aaa/bbb
er virkelig aaa/.
, som igjen egentlig bare er aaa
men med et annet navn. Så vi har disse to scenariene:
-
ccc
eksisterer ikke:Kommandoen
cp -R aaa/. ccc
betyr» opprettccc
og kopier innholdet avaaa/.
tilccc/.
, dvs. kopieraaa
tilccc
. -
ccc
eksisterer:Kommandoen
cp -R aaa/. ccc
betyr» kopier innholdet avaaa/.
ccc/.
, dvs. kopieraaa
tilccc
.
Kommentarer
- Huh, at ‘ er en ny for meg, men det ser ut til å fungere! Har dette dokumentert hvor som helst?
- Jeg ‘ har testet
inputFolder/.
i stedet forinputFolder
og faktisk fungerer det for meg på Windows / Git Bash. (Det fungerer imidlertid ikke med bare en etterfølgende/
, jeg trenger også prikken etter skråstrek).Jeg ga +1 siden det ‘ er interessant, selv om det ‘ sannsynligvis er litt for vanskelig å bruke det i offentlig kode:) - @IlmariJaronen det trenger ikke ‘ å være dokumentert eksplisitt. Følg forklaringen, så ser du ‘ hvordan det bare » følger reglene » .
Svar
Ikke kopier mappen, bare kopier innholdet:
## 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/
På denne måten sørger du begge for at målkatalogen blir opprettet første gang skriptet kjører, og unngår problemet når du kjører det en gang til.
Chris Down påpeker veldig riktig at i bash vil dette hoppe over filer hvis navn starter med en .
. For å unngå dette , kan du kjøre shopt -s dotglob
før du kjører kommandoen ovenfor.
Begge -p
for mkdir
og -R
for cp
er definert av POSIX, så dette skal være perfekt bærbart.
Kommentarer
- Merk: dette vant ‘ t kopierer filer som starter med
.
. I bash kan du brukedotglob
til det. - Merk at denne metoden sannsynligvis vil mislykkes hvis antall katalogoppføringer i inndatamappen er stort, fordi glob-utvidelsen kan overskride den maksimale kommandolinjelengden.
Svar
Prøv -T
alternativ til cp
. Dette eksisterer i GNU coreutils cp
versjon 8.22; det er kanskje ikke bærbart utenfor det.
Kommentarer
- Ja, det ser ut til at dette var akkurat det jeg ønsket:
cp -T
(cp --no-target-directory
). unix.stackexchange.com/questions/94831/… Dessverre min versjon avcp
er mye eldre. - +1 Brukte dette akkurat i dag.
-u
er et flott alternativ å legge til for å gjøre det lat.
Svar
Du kan også bruke rsync
:
rsync -uav inputFolder/ outputFolder/
(legg merke til skråstreken, spesielt etter den første )
Svar
Du kan bruke -t
alternativet for cp
kommando som:
cp -R inputFolder -t outputFolder
nå hvis målmappen ikke eksisterer, vil det kaste en feil:
cp: failed to access ‘outputFolder’: No such file or directory
merknad over kommandoen kopierer inputFolder
sammen med innholdet (og ikke bare innholdet i det)
hvis du vil for å kopiere bare innholdet i inputFolder
blir det litt vanskelig (siden du må være forsiktig når du bruker shell globbing mens du bruker stjerne *)
cp -R -t outputFolder/ -- inputFolder/*
nå hvis målmappen ikke eksisterer, vil det kaste en feil:
cp: failed to access ‘outputFolder’: No such file or directory
fungerer med cp (GNU coreutils) 8.23
Svar
Etter min mening er det ingenting enklere og mer bærbart enn rm -rf outputFolder
. Så jeg holder alltid med det. Jeg forstår at spørsmålet ditt er annerledes, men jeg tror at dette er den beste fremgangsmåten.
Kommentarer
- Det mislykkes hvis det var spesifikke tillatelser eller eierskap på målet som måtte holdes. Eller om det var en symlink.
- Spørsmålet ønsker at resultatet av
cp
skal være det samme, både når katalogen ikke eksisterer og når den eksisterer. Så hva snakker du om? -
cp
-kommandoen gitt av OP holder ikke ‘ t beholder tillatelser (eller tidsstempler).
mv
fra å flytte en samling filer til en enkelt vanlig?rsync
?cp
, rør mellom totar
kommandoer er en fin pålitelig måte å kopiere trær på.rsync
sendt.tar -C input -cf - . | tar -C output -xf -
fungerer.-C
endrer arbeidskatalog, men det er ikke et standardalternativ, så hvis det ‘ ikke støttes, må du kjøre de to feltene i de riktige arbeidsmappene til å begynne med, f.eks( cd input && tar -cf - . ) | ( cd output && tar -xf - )
Men hvis du ‘ har å gjøre med Windows, vil jeg bare bruke roaima ‘ s svar .