Jak skopiować folder rekurencyjnie w idempotentny sposób przy użyciu cp?
On 1 grudnia, 2020 by adminKiedy używam
cp -R inputFolder outputFolder
wynikiem jest zależne od kontekstu :
- jeśli
outputFolder
nie istnieje, będzie zostanie utworzony, a ścieżka sklonowanego folderu będzieoutputFolder
. - Jeśli
outputFolder
istnieje, utworzony klon byćoutputFolder/inputFolder
To jest okropne , ponieważ chcę stworzyć jakiś skrypt instalacyjny i jeśli użytkownik uruchomi go dwukrotnie przez pomyłkę, za pierwszym razem będzie miał outputFolder
, a przy drugim zostać utworzony jeszcze raz w outputFolder/inputFolder
.
- Zawsze chcę pierwsze zachowanie: utwórz klon obok oryginału (jako rodzeństwo).
- Chcę używać
cp
jako przenośnego (np. MINGW tego nie robirsync
wysłane) - Sprawdziłem
cp -R --parents
, ale to odtwarza ścieżkę w górę drzewa katalogów (więc klon nie będzieoutputFolder
, alesome/path/outputFolder
) -
--remove-destination
lub--update
w przypadku, gdy 2 nic nie zmienia, nadal są kopiowane dooutputFolder/inputFolder
Czy można to zrobić bez uprzedniego sprawdzania istnienia outputFolder
(jeśli folder nie istnieje, to …) lub używania rm -rf outputFolder
?
Jaki jest uzgodniony, przenośny sposób na robienie tego w systemie UNIX?
Komentarze
Odpowiedź
Zamiast tego użyj:
cp -R inputFolder/. outputFolder
Działa to dokładnie w taki sam sposób, jak, powiedzmy, cp -R aaa/bbb ccc
działa: jeśli ccc
„nie istnieje” została utworzona jako kopia bbb
i jego zawartości; ale jeśli ccc
już istnieje, to ccc/bbb
jest tworzony jako kopia bbb
i jego zawartości .
Niemal w każdym przypadku bbb
powoduje to niepożądane zachowanie, które odnotowałeś w swoim pytaniu. Jednak w tej konkretnej sytuacji bbb
to po prostu .
, więc aaa/bbb
to naprawdę aaa/.
, co z kolei jest po prostu aaa
, ale pod inną nazwą. Mamy więc dwa scenariusze:
-
ccc
nie istnieje:Polecenie
cp -R aaa/. ccc
oznacza„ utwórzccc
i skopiuj zawartośćaaa/.
doccc/.
, tj. skopiujaaa
doccc
. -
ccc
istnieje:Polecenie
cp -R aaa/. ccc
oznacza” skopiuj zawartośćaaa/.
doccc/.
, tzn. skopiujaaa
doccc
.
Komentarze
- Hmm, to ' to dla mnie nowość, ale wydaje się, że działa! Czy to gdziekolwiek jest udokumentowane?
- ' przetestowałem
inputFolder/.
zamiastinputFolder
i rzeczywiście działa dla mnie na Windows / Git Bash. (Jednak nie działa tylko z końcowym/
, potrzebuję również kropki po ukośniku).Dałem +1, ponieważ ' jest interesujące, chociaż ' jest prawdopodobnie zbyt trudne, aby używać go w publicznym kodzie:) - @IlmariJaronen to nie ' nie musi być jawnie udokumentowane. Postępuj zgodnie z wyjaśnieniem, a ' zobaczysz, jak po prostu ” przestrzega zasad ” .
Odpowiedź
Nie kopiuj folderu, tylko skopiuj zawartość:
## 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/
W ten sposób oboje zapewniacie, że katalog docelowy jest tworzony przy pierwszym uruchomieniu skryptu i unikacie problem przy uruchamianiu go po raz drugi.
Chris Down bardzo poprawnie wskazuje, że w bashu pominie to pliki, których nazwa zaczyna się od .
. Aby tego uniknąć , możesz uruchomić shopt -s dotglob
przed uruchomieniem powyższego polecenia.
Oba -p
dla mkdir
i -R
dla cp
są zdefiniowane przez POSIX, więc powinny być w pełni przenośne.
Komentarze
- Uwaga: nie udało się ' kopiować plików zaczynających się od
.
. W bashu możesz w tym celu użyćdotglob
. - Uwaga: ta metoda może się nie powieść, jeśli liczba pozycji katalogu w folderze wejściowym jest duża, ponieważ rozszerzenie globu może przekroczyć maksymalną długość wiersza poleceń.
Odpowiedź
Wypróbuj -T
opcja cp
. Istnieje w GNU coreutils cp
w wersji 8.22; poza tym może nie być przenośny.
Komentarze
- Tak, wygląda na to, że właśnie tego chciałem:
cp -T
(cp --no-target-directory
). unix.stackexchange.com/questions/94831/… Niestety moja wersjacp
jest znacznie starszy. - +1 Użyłem tego właśnie dzisiaj.
-u
to świetna opcja dodania, aby uczynić ją leniwą.
Odpowiedź
Możesz również użyć rsync
:
rsync -uav inputFolder/ outputFolder/
(zwróć uwagę na ukośniki, zwłaszcza po pierwszym )
Odpowiedź
Możesz użyć opcji -t
cp
polecenie jako:
cp -R inputFolder -t outputFolder
teraz jeśli folder docelowy nie istnieje, wyświetli się błąd:
cp: failed to access ‘outputFolder’: No such file or directory
uwaga powyżej polecenie skopiuje inputFolder
wraz z jego zawartością (a nie tylko zawartością wewnątrz niego)
jeśli chcesz kopiowanie samej zawartości inputFolder
staje się nieco trudne (ponieważ musisz być ostrożny podczas używania powłoki globbingu podczas używania gwiazdki *)
cp -R -t outputFolder/ -- inputFolder/*
teraz, jeśli folder docelowy nie istnieje, wygeneruje błąd:
cp: failed to access ‘outputFolder’: No such file or directory
działa z cp (GNU coreutils) 8.23
Odpowiedź
Moim zdaniem nie ma nic prostszego i bardziej przenośnego niż rm -rf outputFolder
. Więc zawsze się tego trzymam. Rozumiem, że Twoje pytanie jest inne, ale myślę, że jest to najlepsza praktyka.
Komentarze
- To się nie powiedzie, jeśli istnieją określone uprawnienia lub prawa własności na celu, który należało zachować. Lub jeśli był to link symboliczny.
- Pytanie chce, aby wynik
cp
był taki sam, zarówno wtedy, gdy katalog nie istnieje, jak i gdy istnieje. O czym więc mówisz? - Polecenie
cp
podane przez OP nie ' nie zachowuje uprawnień (lub sygnatury czasowe).
mv
przenoszenie zbioru plików do jednego zwykłego?rsync
?cp
, łączenie dwóch poleceńtar
to niezawodny sposób na kopiowanie drzew.rsync
.tar -C input -cf - . | tar -C output -xf -
działa.-C
zmienia katalog roboczy, ale nie jest to opcja standardowa, więc jeśli ' nie jest obsługiwana, musisz uruchomić te dwa tary w odpowiednie katalogi robocze na początek, np( cd input && tar -cf - . ) | ( cd output && tar -xf - )
Jeśli jednak ' masz do czynienia z systemem Windows, użyłbym tylko odpowiedzi roaima ' s .