Dlaczego -r jest rekurencyjny podczas kopiowania katalogu w systemie Linux?

47

Moje pytanie brzmi: dlaczego -rpodczas tworzenia kopii katalogu wymagane jest użycie flagi (rekurencyjnej)? To znaczy, dlaczego to zrobić:

$ cp -r dir1 copyDir1

Kiedy nie chcę tego zachowania podczas kopiowania katalogu?

Czy rekurencyjna kopia katalogu nie jest tak naprawdę „domyślnym” zachowaniem; zachowanie, którego pragniemy prawie cały czas?

Wygląda na to, że jest to zbędna flaga.

Madeleine P. Vincent
źródło
Czy nie musisz też kopiować do niego plików i folderów?
QuyNguyen2013
Jeśli uważasz, że byłoby to ulepszenie, możesz ponownie opublikować tę prośbę na kanale programistów. W przeciwnym razie prawdopodobnie został zaprogramowany dawno temu.
bloger
@blogger Został zaprogramowany dawno temu, ale z jakiegoś powodu. Oznacza to, że jeśli ktoś chce wykonywać podstawową pracę w środowisku wiersza poleceń, jego zadanie powinno być tak proste, że uniknięcie awarii systemu jest trudne. Oznacza to, że istnieją dobre powody, dla których istnieją pewne konwencje interakcji użytkownika z linii poleceń. Rozwijam tę koncepcję w mojej odpowiedzi.
JakeGould
2
To pytanie zostało zadane i odpowiedzi na unix.SE .
dotancohen
To samo dotyczyrm

Odpowiedzi:

58

Jak działają systemy plików, katalog nie jest tak naprawdę folderem zawierającym pliki, ale katalog jest plikiem zawierającym wskaźniki i-węzłów do podłączonych do niego plików „potomnych”. Oznacza to, że z perspektywy systemu plików plik jest plikiem, ale katalog to tylko plik zawierający listę połączonych plików.

Więc z perspektywy wiersza poleceń, robiąc to:

$ cp dir1 copyDir1

Zasadniczo oznaczałoby to skopiowanie pliku o nazwie dir1do nowego pliku o nazwie copyDir1. A jeśli chodzi o system plików, dir1to i tak jest tylko plikiem; fakt, że jest to „katalog”, będzie widoczny tylko wtedy, gdy system plików faktycznie sprawdzi, czy rzeczywiście dir1jest to stos bitów.

-rFlaga informuje system plików rekursywnie rozwijanego drzewa pliku / katalogu i skopiować całą zawartość i żadnych, które mogą być „dzieckiem” tego pliku do nowego miejsca.

Teraz, dlaczego może się to wydawać zbędne lub zbędne, tak naprawdę sprowadza się to do historycznych metod radzenia sobie z systemami plików. Oprócz tworzenia systemu, który jest bezpieczny od wszelkiego rodzaju błędów związanych z użytkownikiem; przypadkowe i celowe.

To znaczy, załóżmy, że masz ~/binplik w katalogu domowym, który chcesz skopiować, ale przypadkowo ~pominąłeś - ponieważ jesteś człowiekiem i popełniasz błędy - więc /binwygląda to tak:

cp /bin/ ~/copy_of_bin

Dzięki „siatce bezpieczeństwa” /binbycia katalogiem w połączeniu z potrzebą -rflagi unikniesz przypadkowego skopiowania całego binarnego katalogu głównego systemu, w którym jesteś, do katalogu domowego. Gdyby ta siatka bezpieczeństwa nie istniała, nastąpiłaby drobna - lub być może poważna - katastrofa.

Logika polega na tym, że w dniach poprzedzających GUI (graficzne interfejsy użytkownika) należy ustawić konwencje logiczne / behawioralne, aby uniknąć tworzenia przez użytkownika wpadek, które potencjalnie mogą zabić system. A używanie -rflagi jest teraz jednym z nich.

Jeśli wydaje się to zbyteczne, nie musisz szukać dalej niż nowoczesny system GUI, który można umieścić nad systemami plików Linux. GUI rozwiązuje podstawowe problemy użytkownika, takie jak ten, umożliwiając łatwe przeciąganie i upuszczanie plików i katalogów.

Ale w przypadku interfejsów tekstowych wiele „wrażeń użytkownika” w tym świecie to po prostu logiczne i oparte na hueristic guzy drogowe, które pomagają utrzymać kontrolę nad użytkownikiem, aby zapobiec potencjalnej katastrofie.

Podobnie właśnie dlatego systemy plików Linux / Unix nie mają domyślnie ustawionych 777uprawnień i sudouprawnień oraz w jaki sposób prawdziwi administratorzy systemu drżą, gdy użytkownik ustawia 777uprawnienia lub przyznaje wszystkim sudoprawa. Są to podstawowe czynności, które należy wykonać, aby zapewnić stabilność systemu i jak najbardziej „dowód użytkownika”; ktokolwiek spieszy się, by zewrzeć te konwencje, najprawdopodobniej spowoduje uszkodzenie swojego systemu, nawet o tym nie wiedząc.

INFORMACJE DODATKOWE: Inna odpowiedź tutaj na stronie Unix Stack Exchange daje dobre wyjaśnienie, dlaczego nierekurencyjna kopia katalogu jest problematyczna; nacisk jest mój.

Cóż, bez flagi -R, możliwe jest tylko kopiowanie plików, ponieważ jest raczej rzadkie, że ktoś chce nierekurencyjnie skopiować katalog: Kopia nierekurencyjna po prostu dałaby drugą nazwę katalogu, wskazując bezpośrednio na ta sama struktura katalogów. Ponieważ ludzie rzadko tego chcą, a tak naprawdę istnieje osobny program (ln), nierekurencyjna kopia katalogów jest niedozwolona.

Jeśli więc katalog jest tak naprawdę plikiem z elementami i-węzłów, wykonanie prostej kopii tego pliku byłoby równoznaczne z działaniem twardego łącza. Którego nie chce.

JakeGould
źródło
19
Osobiście uważam, że aspekt „ochronny” nie przechodzi testu węchu. Pewien człowiek mógłby równie łatwo pisać cp -r /binjak cp-r ~/bin. Sama flaga nie zapobiega błędom ani niekoniecznie poprawia ostrożność. Jeśli chcesz zapobiec błędom, polecenie cp może równie łatwo spojrzeć na dany węzeł i wyświetlić monit, coś w stylu „To jest katalog, czy chcesz skopiować całą zawartość do określonej lokalizacji (y / n)? ” To byłaby siatka bezpieczeństwa . Wymaganie -r dla katalogów utrzymuje nadmiar kodu w dół bardziej niż cokolwiek innego.
JDL
12
Zła analogia do włazu. Jak powiedział @JDL, wspomniana flaga nic nie zapobiega literówce na ścieżce. Z przyjemnością przyjmę spójność z innymi poleceniami jako powód, ale mam wrażenie, że prawdziwym powodem jest to, że „tak to było najpierw napisane, a teraz tak wiele rzeczy polega na tym zachowaniu, zmiana jest niemożliwa”.
Podstawowy
7
Kiedy przenosimy katalog, nie potrzebujemy -r. Myślę, że połączona odpowiedź na unix.stackexchange.com jest znacznie bardziej istotna. Odpowiednikiem nierekurencyjnej kopii byłoby posiadanie drugiego katalogu i twardych dowiązań do wszystkich plików w drzewie katalogów.
gerrit
2
Jeśli się nie mylę, -r było rozszerzeniem GNU - nie sądzę, żeby historyczny UNIX cp miał rekurencyjną kopię - i to było częścią powodu polecenia rsync .
Mei
2
@JakeGould Rozumiem podstawowe pojęcia. Ja jednak nie zgadzam się z ideą, że flaga -r w danym poleceniu zapewnia dodatkowe bezpieczeństwo. Z mojego doświadczenia wynika, że ​​polecenia Linuksa z linii poleceń są z natury niebezpieczne. Bezpieczeństwo zostało dodane w miarę upływu czasu. Bezpieczeństwo związane z projektowaniem linuksa pochodzi z systemu uprawnień i rezygnacji z uruchamiania jako root. Niedziałanie jako root jest również czymś, co jest bardziej konwencją niż projektem. Konwencja jest obsługiwana w większości nowych instalatorów Linuksa, ale nie zawsze była.
JDL
19

To prawda, że ​​takiego zachowania pragniemy prawie cały czas. Nie musi to jednak oznaczać, że kopiowanie rekurencyjne powinno być zachowaniem domyślnym.

Myślę, że przyczyny cpdziałają tak, jak mają swoje korzenie w filozofii Uniksa . Unix faworyzuje programy, które robią jedną rzecz i robią to dobrze , a także programy, które są proste zarówno pod względem interfejsu, jak i implementacji (czasem nazywane gorszym jest lepsze ).

Kluczowym elementem łamigłówki jest to, że cpnie kopiuje katalogów - cpkopiuje pliki (i tylko pliki). Jeśli chcesz skopiować katalog, cp wywołuje się rekurencyjnie , aby skopiować pliki z każdego katalogu.

Oczywiście różnica między „kopiowaniem katalogów” a „kopiowaniem plików rekurencyjnie” jest absolutnie niczym, z punktu widzenia użytkownika, ale posiadanie tego interfejsu pomaga zachować prostotę implementacji .

Jeśli cpudało Ci się skopiować katalogi, wkrótce pojawi się pokusa, aby dodać więcej funkcji, które mają sens tylko dla katalogów - na przykład możesz chcieć kopiować tylko nazwy plików, które się zakończyły .sh. Nieuchronnie prowadzi to do rozdęcia i pełzania funkcji , do których jesteśmy przyzwyczajeni w innych systemach operacyjnych - powodując, że oprogramowanie jest powolne, złożone i podatne na błędy.

Kolejną zaletą jest to, że -rpomaga również użytkownikowi zrozumieć, co naprawdę dzieje się pod interfejsem. Przyjemnym efektem ubocznym jest to, że nauczenie się pojęcia operacji rekurencyjnej zaoszczędzi ci trochę pracy, gdy dowiesz się o innych narzędziach, które ją obsługują (takich jak grepna przykład)


Niektóre osoby z pewnością powiedzą ci, że ujawnianie szczegółów implementacji użytkownikowi jest złe , a posiadanie większej liczby funkcji jest dobre . Moim celem jest jedynie wyjaśnienie uzasadnienia tego zachowania, więc nie będę się starał kłócić w żaden sposób.

goncalopp
źródło
2
+1 „… zrób jedną rzecz i zrób to dobrze…” Dziękujemy za stwierdzenie tego!
JakeGould
5

Interakcje z katalogami zapewniają, że wiesz, że wchodzisz w interakcję z katalogiem, a NIE z pojedynczym plikiem.

Na przykład:

$ tree
.
└── folder1
    └── sub1
        └── subsub1

3 directories, 0 files
$
$ cp folder1/ folder2
cp: folder1/ is a directory (not copied).
$
$ mkdir blah
$ cp blah/ blah2
cp: blah/ is a directory (not copied).
$ rm blah/
rm: blah/: is a directory

Tak więc, jeśli chcesz pomyślnie skopiować folder, ponieważ oznacza on zarówno folder, jak i obiekty związane z odwoływaniem się do folderu, musisz traktować go jak kolekcję plików:

$ cp -r folder1/ folder2
$ rm -rf folder1
Mbb
źródło
3

Konsekwencją zmiany wartości domyślnej byłoby uszkodzenie tysięcy skryptów powłoki. Prowadzi to do wymagań POSIX i SUS dla dobrze znanego domyślnego zachowania.

Powodem jest historyczny rozwój komend cp, ln i mv (wszystkie te same pliki binarne w większości starych systemów UNIX) w różnych gałęziach UNIX. Kiedy się -rpojawił (wcześnie cpnie miał opcji kopiowania katalogów; tutaj jest wczesna strona podręcznika man bez -rlub -R), występowały różne różnice w obsłudze specjalnych plików, dowiązań symbolicznych i innych błędów systemu plików.

Ze specyfikacji bazowej Open Group Issue 7 :

Wcześniejsze wersje tego standardu zawierały opcję -r do kopiowania hierarchii plików. Opcja -r jest historyczną praktyką w systemach BSD i pochodnych BSD. Ta opcja nie jest już określona przez POSIX.1-2008, ale może być obecna w niektórych implementacjach. Opcja -R została dodana jako ścisły synonim opcji -r, wybrana ze względu na spójność ze wszystkimi innymi opcjami w tym tomie POSIX.1-2008, które wykonują rekurencyjne opadanie katalogu.

Różnica między opcją -R a usuniętą opcją -r polega na traktowaniu przez cp typów plików innych niż zwykły i katalogowy. Zdefiniowano implementację, w jaki sposób opcja - traktowała specjalne pliki, aby umożliwić zarówno implementacje historyczne, jak i te, które wybrały obsługę opcji -r z tymi samymi zdolnościami co opcja -R zdefiniowana w tym tomie POSIX.1-2008. Oryginalna flaga -r, z przyczyn historycznych, nie traktowała plików specjalnych inaczej niż zwykłe pliki, ale zawsze odczytywała plik i kopiowała jego zawartość. Miało to oczywiste problemy w obecności specjalnych typów plików; na przykład urządzenia postaci, FIFO i gniazda.

W rzeczywistości nadal będziesz widzieć niektóre osoby regularnie używające:

cd dir1 ; tar -cf - . | (cd dir2 ; tar -xpf -)

Ponieważ nie ufają, że cp -rimplementacja jest tym, do czego są przyzwyczajeni na dowolnym komputerze; Lub dlatego, że chcą takiego tarzachowania.

kael
źródło
3

Dzisiaj może to być nieoptymalny interfejs użytkownika, ale decyzja została podjęta około 1970 roku podczas projektowania systemu UNIX, kiedy dysk był znacznie droższy. Miliony skryptów powłoki zależą od tego, czy działa w ten sposób, i jest o wiele za późno, aby to zmienić.

Zobacz ten artykuł, aby uzyskać oryginalne informacje o projekcie.

pjc50
źródło
3

Oczywistą zaletą tej -rflagi jest to, że można cp * /target/dirskopiować tylko wszystkie pliki z katalogu źródłowego do katalogu docelowego, pomijając (choć z ostrzeżeniem) wszystkie zawarte w nim katalogi. cp -r * /target/dirskopiuje wszystko, łącznie z podkatalogami.

Interneci
źródło
2

Ta flaga jest potrzebna tylko wtedy, gdy cpjest to polecenie do kopiowania plików i katalogów, a nie tylko katalogów.

Gdyby istniało specjalne polecenie dla kopii katalogów, zachowanie „domyślne” byłoby z pewnością kopią rekurencyjną.

koleś
źródło
1
Ma sens. Ale dlaczego ktoś miałby kopiować katalog, w którym nie ma co najmniej jednego pliku? Dlaczego więc nie skorzystać mkdir?
JakeGould
1
@JakeGould może dlatego, że mogą potrzebować zachować własność i uprawnienia?
Ruslan
1

Jak wspomnieli inni, katalog to po prostu inny typ pliku (w przeciwieństwie do zwykłego pliku), który zwykle „zawiera” (wskazuje) inne pliki. Może zawierać podkatalogi, których dotyczy to samo ...

Więc jeśli kopiujesz katalog (perspektywa użytkownika), tak naprawdę kopiujesz kilka plików (perspektywa systemu plików) (zwykłe pliki, pliki katalogu, dowiązania symboliczne, ...) i dla każdego pliku katalogu rekurencyjnie to powtarzasz proces. Ponieważ kopiowanie katalogu jest z definicji procesem rekurencyjnym, wywoływany jest argument cp --recursive.

Oczywiście bardzo łatwo jest utworzyć skrót poleceń w środowisku użytkownika (umieść go w pliku .profile / .bashrc, aby był stale dostępny):

alias cpr='cp -r'

A może lepiej:

alias cpa='cp -av'

W ten sposób możesz skopiować katalog za pomocą cpa dir1 copyDir1i nie tylko wydrukuje to, co jest kopiowane, ale także zastosuje uprawnienia do plików.

A ponieważ ktoś wspomniał, że CP może teoretycznie wykryć, że plik źródłowy jest katalogiem i zapytać, czy powinien go skopiować rekurencyjnie, oto krótka sugestia:

cp()
{
    if [ ! -e "$1" ]; then
        echo missing source file
        return 1
    fi
    arg="-d --preserve=all -v"
    if [ -d "$1" ]; then
        read -p "Copy directory recursively? " -n 1 -r
        if [ "$REPLY" == "y" ]; then
            arg="$arg -r"
        fi
        echo
    fi
    /usr/bin/cp $arg "$@"
}

To tylko tanie opakowanie CP. Zawsze zachowuje wszystkie metadane (tj. Kopiuje czas modyfikacji pliku, poprawnie kopiuje dowiązania symboliczne itd.), A jeśli próbujesz skopiować katalog, pyta, czy powinien go skopiować (rekurencyjnie).

podstawowy 6
źródło