Hur kopierar jag en mapp rekursivt på ett idempotent sätt med cp?
On december 1, 2020 by adminNär jag använder
cp -R inputFolder outputFolder
blir resultatet kontextberoende :
- om
outputFolder
inte finns kommer det skapas och den klonade mappsökvägen bliroutputFolder
. - om
outputFolder
existerar kommer den skapade klonen att varaoutputFolder/inputFolder
Detta är hemskt , för att jag vill skapa ett installationsskript och om användaren av misstag kör det två gånger kommer han att skapa outputFolder
första gången, då kommer andra grejerna vid andra körningen skapas igen i outputFolder/inputFolder
.
- Jag vill alltid ha det första beteendet: skapa en klon bredvid originalet (som ett syskon).
- Jag vill använda
cp
för att vara bärbar (t.ex. MINGW gör det inte harrsync
levererat) - Jag markerade
cp -R --parents
men detta återskapar banan hela vägen upp i katalogträdet (så klonen är inteoutputFolder
mensome/path/outputFolder
) -
--remove-destination
eller--update
om 2 inte ändrar någonting, fortfarande kopieras saker tilloutputFolder/inputFolder
Är det finns ett sätt att göra detta utan att först kontrollera om det finns outputFolder
(om mappen inte finns då …) eller använda rm -rf outputFolder
?
Vad är det överenskomna, bärbara UNIX-sättet att göra det?
Kommentarer
Svar
Använd detta istället:
cp -R inputFolder/. outputFolder
Detta fungerar på exakt samma sätt som, säg, cp -R aaa/bbb ccc
fungerar: om ccc
existerar inte ” s skapas som en kopia av bbb
och dess innehåll; men om ccc
redan finns skapas ccc/bbb
som en kopia av bbb
.
För nästan alla förekomster av bbb
ger detta det oönskade beteende som du noterade i din fråga. I denna specifika situation är bbb
bara .
, så aaa/bbb
är verkligen aaa/.
, som i sin tur egentligen bara är aaa
men med ett annat namn. Så vi har dessa två scenarier:
-
ccc
existerar inte:Kommandot
cp -R aaa/. ccc
betyder” skapaccc
och kopiera innehållet iaaa/.
tillccc/.
, dvs. kopieraaaa
tillccc
. -
ccc
finns:Kommandot
cp -R aaa/. ccc
betyder” kopiera innehållet iaaa/.
ccc/.
, dvs. kopieraaaa
tillccc
.
Kommentarer
- Huh, att ’ är en ny för mig, men det verkar fungera! Har det här dokumenterats någonstans?
- Jag ’ har testat
inputFolder/.
istället förinputFolder
och det fungerar verkligen för mig på Windows / Git Bash. (Det fungerar dock inte med bara en efterföljande/
, jag behöver också punkten efter snedstrecket).Jag gav +1 eftersom det ’ är intressant, även om det ’ är förmodligen lite för knepigt att använda det i offentlig kod:) - @IlmariJaronen behöver inte ’ inte dokumenteras uttryckligen. Följ förklaringen så ser du ’ hur det helt enkelt ” följer reglerna ” .
Svar
Kopiera inte mappen, bara kopiera innehållet:
## 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å så sätt ser du till att målkatalogen skapas första gången skriptet körs och undviker problem när den körs en andra gång.
Chris Down påpekar mycket korrekt att i bash kommer detta att hoppa över filer vars namn börjar med en .
. För att undvika detta kan du köra shopt -s dotglob
innan du kör kommandot ovan.
Båda -p
för mkdir
och -R
för cp
definieras av POSIX så detta ska vara perfekt bärbart.
Kommentarer
- Obs: detta vann ’ t kopierar filer som börjar med
.
. I bash kan du användadotglob
för det. - Observera att denna metod sannolikt kommer att misslyckas om antalet katalogposter i inmatningsmappen är stort, eftersom glob-expansionen kan överskrida den maximala kommandoradslängden.
Svar
Testa -T
alternativ till cp
. Detta finns i GNU coreutils cp
version 8.22; det kanske inte är bärbart utanför det.
Kommentarer
- Ja, det verkar som att det är precis vad jag ville ha:
cp -T
(cp --no-target-directory
). unix.stackexchange.com/questions/94831/… Tyvärr min version avcp
är mycket äldre. - +1 Används detta just idag.
-u
är ett bra alternativ att lägga till för att göra det lat.
Svar
Du kan också använda rsync
:
rsync -uav inputFolder/ outputFolder/
(notera snedstreck, särskilt efter den första )
Svar
Du kan använda -t
alternativet för cp
kommando som:
cp -R inputFolder -t outputFolder
nu om målmappen inte finns kommer det att kasta ett fel:
cp: failed to access ‘outputFolder’: No such file or directory
kommandot ovan kommer att kopiera inputFolder
tillsammans med dess innehåll (och inte bara innehållet i det)
om du vill för att bara kopiera innehållet i inputFolder
blir det lite knepigt (eftersom du måste vara försiktig när du använder skalkolv medan du använder asterisk *)
cp -R -t outputFolder/ -- inputFolder/*
nu om målmappen inte finns kommer det att ge ett fel:
cp: failed to access ‘outputFolder’: No such file or directory
fungerar med cp (GNU coreutils) 8.23
Svar
Enligt min mening finns det inget enklare och mer portabelt än rm -rf outputFolder
. Så jag håller alltid med det. Jag förstår att din fråga är annorlunda, men jag tycker att detta är den bästa metoden.
Kommentarer
- Det misslyckas om det fanns specifika behörigheter eller äganderätt på det mål som behövde hållas. Eller om det var en symlink.
- Frågan vill att resultatet av
cp
ska vara detsamma, både när katalogen inte finns och när den finns. Så, vad pratar du om? -
cp
-kommandot enligt OP ger inte ’ t behörighet (eller tidsstämplar).
mv
flyttar en samling filer till en enda vanlig?rsync
?cp
, piping mellan tvåtar
-kommandon är ett trevligt pålitligt sätt att kopiera träd.rsync
skickat.tar -C input -cf - . | tar -C output -xf -
fungerar.-C
ändrar arbetskatalogen, men det är inte ett standardalternativ, så om det ’ inte stöds måste du köra de två delarna i rätt arbetskataloger till att börja med, t.ex.( cd input && tar -cf - . ) | ( cd output && tar -xf - )
Men om du ’ har att göra med Windows skulle jag bara använda roaima ’ s svar .