Jak kopírovat složku rekurzivně idempotentním způsobem pomocí cp?
On 1 prosince, 2020 by adminKdyž použiji
cp -R inputFolder outputFolder
výsledek je kontextově závislý :
- pokud
outputFolder
neexistuje, bude vytvořena a cesta klonované složky budeoutputFolder
. - pokud
outputFolder
existuje, vytvořený klon bude beoutputFolder/inputFolder
To je hrozné , protože chci vytvořit nějaký instalační skript, a pokud jej uživatel omylem spustí dvakrát, bude mít outputFolder
vytvořen poprvé, pak při druhém spuštění budou všechny věci být znovu vytvořen v outputFolder/inputFolder
.
- Chci vždy první chování: vytvořit klon vedle originálu (jako sourozenec).
- Chci použít
cp
k přenosu (např. MINGW ne mítrsync
odesláno) - Zkontroloval jsem
cp -R --parents
, ale tím se znovu vytvoří cesta až k adresářovému stromu (takže klon nebudeoutputFolder
alesome/path/outputFolder
) -
--remove-destination
nebo--update
v případě 2 nic nezmění, stále se věci zkopírují dooutputFolder/inputFolder
Je existuje způsob, jak to udělat, aniž byste nejprve zkontrolovali existenci outputFolder
(pokud složka neexistuje …) nebo pomocí rm -rf outputFolder
?
Jaký je dohodnutý přenosný způsob UNIXu?
Komentáře
Odpověď
Použijte místo toho:
cp -R inputFolder/. outputFolder
Funguje to přesně stejným způsobem, jako například cp -R aaa/bbb ccc
: pokud ccc
neexistuje, pak “ s vytvořen jako kopie bbb
a jeho obsahu; ale pokud ccc
již existuje, je ccc/bbb
vytvořen jako kopie bbb
a jeho obsahu .
Pro téměř jakoukoli instanci bbb
to dává nežádoucí chování, které jste si všimli ve své otázce. V této konkrétní situaci však bbb
je pouze .
, takže aaa/bbb
je opravdu aaa/.
, což je zase jen aaa
, ale pod jiným jménem. Máme tedy tyto dva scénáře:
-
ccc
neexistuje:Příkaz
cp -R aaa/. ccc
znamená“ vytvořitccc
a zkopírovat obsahaaa/.
doccc/.
, tj. zkopírujteaaa
doccc
. -
ccc
existuje:Příkaz
cp -R aaa/. ccc
znamená“ zkopírovat obsahaaa/.
doccc/.
, tj. zkopírujteaaa
doccc
.
Komentáře
- Huh, ten ‚ je pro mě nový, ale zdá se, že funguje! Bylo to zdokumentováno kdekoli?
- Testoval jsem
inputFolder/.
místoinputFolder
. a ve skutečnosti to funguje pro Windows / Git Bash. (To však nefunguje jen s koncovým/
, potřebuji také tečku za lomítkem).Dal jsem +1, protože je to ‚ zajímavé, i když je to ‚ pravděpodobně příliš složité na to, aby se dalo použít ve veřejném kódu :))
Odpověď
Nekopírujte složku, pouze zkopírujte obsah:
## 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/
Tímto způsobem oba zajistíte vytvoření cílového adresáře při prvním spuštění skriptu a vyhnete se problém při druhém spuštění.
Chris Down velmi správně zdůrazňuje, že v bash bude přeskočeno soubory, jejichž název začíná na .
. Abyste tomu zabránili , můžete spustit shopt -s dotglob
před spuštěním výše uvedeného příkazu.
Oba -p
pro mkdir
a -R
pro cp
jsou definovány POSIXem, takže by měl být dokonale přenosný.
Komentáře
- Poznámka: toto nebude ‚ kopírovat soubory začínající
.
. V bash můžete k tomu použítdotglob
. - Všimněte si, že tato metoda pravděpodobně selže, pokud je počet položek adresáře ve vstupní složce velký, protože globální expanze může překročit maximální délku příkazového řádku.
Odpověď
Vyzkoušejte -T
možnost cp
. Toto existuje v GNU coreutils cp
verze 8.22; mimo to nemusí být přenosné.
Komentáře
- Ano, zdá se, že to je přesně to, co jsem chtěl:
cp -T
(cp --no-target-directory
). unix.stackexchange.com/questions/94831/… Bohužel moje verzecp
je mnohem starší. - +1 Používáno právě dnes.
-u
je skvělá volba, jak přidat, aby byla líná.
Odpovědět
Můžete také použít rsync
:
rsync -uav inputFolder/ outputFolder/
(všimněte si lomítek, zejména po prvním )
Odpověď
Můžete použít -t
možnost cp
příkaz jako:
cp -R inputFolder -t outputFolder
nyní, pokud cílová složka neexistuje, vyvolá chybu:
cp: failed to access ‘outputFolder’: No such file or directory
poznámka nad příkazem zkopíruje inputFolder
spolu s jeho obsahem (nejen obsahem uvnitř)
pokud chcete zkopírovat pouze obsah inputFolder
je to trochu složité (protože při používání globingu prostředí při použití hvězdičky * musíte být opatrní)
cp -R -t outputFolder/ -- inputFolder/*
nyní, pokud cílová složka neexistuje, způsobí chybu:
cp: failed to access ‘outputFolder’: No such file or directory
funguje s cp (GNU coreutils) 8.23
Odpověď
Podle mého názoru neexistuje nic jednoduššího a přenositelnějšího než rm -rf outputFolder
. Takže se toho vždy držím. Chápu, že vaše otázka je jiná, ale myslím si, že toto je nejlepší postup.
Komentáře
- To selže, pokud existují konkrétní oprávnění nebo vlastnictví na cíl, který bylo třeba udržet. Nebo pokud to byl symbolický odkaz.
- Tato otázka chce, aby byl výsledek
cp
stejný, a to jak v případě, že adresář neexistuje, tak i v případě, že existuje. O čem to tedy mluvíte? - Příkaz
cp
, jak jej uvádí OP, ‚ nezachovává oprávnění (nebo časová razítka).
mv
v přesunutí kolekce souborů do jednoho normálního?rsync
?cp
, propojení mezi dvěmatar
příkazy je pěkný spolehlivý způsob kopírování stromů.rsync
odesláno.tar -C input -cf - . | tar -C output -xf -
funguje.-C
změní pracovní adresář, ale nejde o standardní možnost, takže pokud to ‚ není podporováno, musíte spustit dva tary v správné pracovní adresáře pro začátek, např( cd input && tar -cf - . ) | ( cd output && tar -xf - )
Pokud však ‚ řešíte Windows, použil bych pouze odpověď roaima ‚ .