Hoe kopieer ik een map recursief op een idempotente manier met cp?
Geplaatst op december 1, 2020 door adminWanneer ik
cp -R inputFolder outputFolder
gebruik, is het resultaat contextafhankelijk :
- als
outputFolder
niet bestaat, zal het aangemaakt, en het gekloonde mappad isoutputFolder
. - als
outputFolder
bestaat, zal de gemaakte kloon beoutputFolder/inputFolder
Dit is vreselijk , omdat ik een installatiescript wil maken, en als de gebruiker het per ongeluk twee keer uitvoert, zal hij outputFolder
de eerste keer hebben aangemaakt, en bij de tweede keer draaien al het spul opnieuw aangemaakt worden in outputFolder/inputFolder
.
- Ik wil altijd het eerste gedrag: maak een kloon naast het origineel (als broer of zus).
- Ik wil
cp
gebruiken om draagbaar te zijn (bijv. MINGW doet dat niet hebbenrsync
verzonden) - Ik heb
cp -R --parents
gecontroleerd, maar dit herschept het pad helemaal omhoog in de mappenboom (dus de kloon is nietoutputFolder
maarsome/path/outputFolder
) -
--remove-destination
of--update
in geval 2 niets verandert, worden de dingen toch gekopieerd naaroutputFolder/inputFolder
Is er is een manier om dit te doen zonder eerst te controleren of de outputFolder
bestaat (als de map niet bestaat dan …) of door rm -rf outputFolder
te gebruiken ?
Wat is de overeengekomen, draagbare UNIX-manier om dat te doen?
Opmerkingen
Antwoord
Gebruik dit in plaats daarvan:
cp -R inputFolder/. outputFolder
Dit werkt op precies dezelfde manier als bijvoorbeeld cp -R aaa/bbb ccc
werkt: als ccc
niet “bestaat, dan is het” s gemaakt als een kopie van bbb
en zijn inhoud; maar als ccc
al bestaat, wordt ccc/bbb
gemaakt als de kopie van bbb
en de inhoud ervan .
Voor bijna elke instantie van bbb
geeft dit het ongewenste gedrag dat u in uw vraag hebt opgemerkt. In deze specifieke situatie is de bbb
echter slechts .
, dus aaa/bbb
is echt aaa/.
, wat op zijn beurt eigenlijk gewoon aaa
is, maar met een andere naam. We hebben dus deze twee scenarios:
-
ccc
bestaat niet:Het commando
cp -R aaa/. ccc
betekent” maakccc
en kopieer de inhoud vanaaa/.
naarccc/.
, dwz kopieeraaa
naarccc
. -
ccc
bestaat wel:Het commando
cp -R aaa/. ccc
betekent” kopieer de inhoud vanaaa/.
naarccc/.
, dwz kopieeraaa
naarccc
.
Reacties
- Huh, dat ‘ is een nieuwe voor mij, maar het lijkt te werken! Is dit ergens gedocumenteerd?
- Ik ‘ heb de
inputFolder/.
getest in plaats vaninputFolder
en het werkt inderdaad voor mij op Windows / Git Bash. (Het werkt echter niet met alleen een afsluitende/
, ik heb ook de punt na de schuine streep nodig).Ik heb +1 gegeven omdat het ‘ interessant is, hoewel het ‘ waarschijnlijk een beetje te lastig is om het in openbare code te gebruiken:) - @IlmariJaronen het hoeft niet ‘ expliciet te worden gedocumenteerd. Volg de uitleg en je ‘ zult zien hoe het ” de regels volgt ” .
Answer
Kopieer de map niet, kopieer alleen de inhoud:
## 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/
Op deze manier zorgt u er allebei voor dat de doeldirectory wordt aangemaakt de eerste keer dat het script wordt uitgevoerd en vermijdt u de probleem wanneer het een tweede keer wordt uitgevoerd.
Chris Down wijst er zeer terecht op dat in bash bestanden worden overgeslagen waarvan de naam begint met een .
. Om dit te vermijden , kunt u shopt -s dotglob
uitvoeren voordat u de bovenstaande opdracht uitvoert.
Beide -p
voor mkdir
en -R
voor cp
worden gedefinieerd door POSIX, dus dit zou perfect draagbaar moeten zijn.
Opmerkingen
- Opmerking: hierdoor wordt ‘ niet gekopieerd van bestanden die beginnen met
.
. In bash kun je daarvoordotglob
gebruiken. - Merk op dat deze methode waarschijnlijk zal mislukken als het aantal directory-items in de invoermap groot is, omdat de glob-uitbreiding kan de maximale lengte van de opdrachtregel overschrijden.
Antwoord
Probeer de -T
optie naar cp
. Dit bestaat in GNU coreutils cp
versie 8.22; het is daarbuiten misschien niet draagbaar.
Reacties
- Ja, het lijkt erop dat dit precies is wat ik wilde:
cp -T
(cp --no-target-directory
). unix.stackexchange.com/questions/94831/… Helaas is mijn versie vancp
is veel ouder. - +1 Gebruikt dit net vandaag.
-u
is een geweldige optie om toe te voegen om het lui te maken.
Antwoord
Je kunt ook rsync
:
rsync -uav inputFolder/ outputFolder/
gebruiken (let op de schuine strepen, vooral na de eerste )
Antwoord
U kunt de -t
optie van cp
commando als:
cp -R inputFolder -t outputFolder
als de doelmap nu niet bestaat, wordt er een fout gegenereerd:
cp: failed to access ‘outputFolder’: No such file or directory
opmerking boven commando zal inputFolder
samen met zijn inhoud kopiëren (en niet alleen de inhoud erin)
als je wilt om alleen de inhoud van inputFolder
te kopiëren, wordt het een beetje lastig (aangezien je voorzichtig moet zijn bij het gebruik van shell-globbing bij het gebruik van asterisk *)
cp -R -t outputFolder/ -- inputFolder/*
als de doelmap nu niet bestaat, wordt er een fout gegenereerd:
cp: failed to access ‘outputFolder’: No such file or directory
werkt met cp (GNU coreutils) 8.23
Answer
Naar mijn mening is er niets eenvoudiger en draagbaarder dan rm -rf outputFolder
. Dus daar blijf ik altijd bij. Ik begrijp dat uw vraag anders is, maar ik denk dat dit de beste praktijk is.
Opmerkingen
- Dat mislukt als er specifieke machtigingen of eigendommen waren op het doel dat bewaard moest worden. Of als het een symlink was.
- De vraag wil dat het resultaat van
cp
hetzelfde is, zowel wanneer de directory niet bestaat als wanneer deze wel bestaat. Dus, waar heb je het over? - Het
cp
commando zoals gegeven door het OP houdt geen ‘ rechten vast (of tijdstempels).
mv
een verzameling bestanden naar één gewone bestanden verplaatst?rsync
niet gebruiken?cp
moeten gebruiken, is piping tussen tweetar
-opdrachten een mooie betrouwbare manier om bomen te kopiëren.rsync
niet heeft verzonden.tar -C input -cf - . | tar -C output -xf -
werkt.-C
verandert de werkdirectory, maar het is geen standaardoptie, dus als het ‘ niet wordt ondersteund, moet je de twee tars uitvoeren in de juiste werkmappen om mee te beginnen, bijv( cd input && tar -cf - . ) | ( cd output && tar -xf - )
Als je echter ‘ met Windows te maken hebt, zou ik gewoon roaima ‘ s antwoord gebruiken .