Warum ist -r rekursiv erforderlich, wenn ein Verzeichnis unter Linux kopiert wird?

47

Meine Frage ist, warum es erforderlich ist, das -r(rekursive) Flag zu verwenden, wenn Sie eine Kopie eines Verzeichnisses erstellen? Das heißt, warum dies tun:

$ cp -r dir1 copyDir1

Wann möchte ich dieses Verhalten beim Kopieren eines Verzeichnisses nicht?

Ist eine rekursive Kopie eines Verzeichnisses nicht wirklich das „Standardverhalten“? das Verhalten, das wir fast die ganze Zeit wollen?

Es fühlt sich so an, als wäre dies eine überflüssige Flagge.

Madeleine P. Vincent
quelle
Müssten Sie die Dateien und Ordner nicht auch kopieren?
QuyNguyen2013
Wenn Sie der Meinung sind, dass dies eine Verbesserung wäre, können Sie diese Anfrage auf dem Entwicklerkanal erneut veröffentlichen. Ansonsten wurde es wahrscheinlich schon vor langer Zeit programmiert.
Blogger
@blogger Es wurde vor langer Zeit programmiert, aber aus einem Grund. Das heißt, wenn jemand grundlegende Arbeiten in einer Befehlszeilenumgebung ausführen möchte, sollte seine Aufgabe nur so einfach sein, wie es schwierig ist, Systemausfälle zu vermeiden. Das bedeutet, dass es gute Gründe dafür gibt, dass einige Konventionen für die Benutzerinteraktion in der Befehlszeile bestehen. In meiner Antwort gehe ich auf dieses Konzept ein.
JakeGould
2
Diese Frage wurde unter unix.SE gestellt und beantwortet .
Dotancohen
Gleiches gilt fürrm

Antworten:

58

So wie Dateisysteme funktionieren, ist ein Verzeichnis eigentlich kein Ordner, der Dateien enthält, sondern ein Verzeichnis ist eine Datei, die Inode-Zeiger auf „untergeordnete“ Dateien enthält, die mit ihm verbunden sind. Aus Sicht des Dateisystems ist eine Datei eine Datei, ein Verzeichnis jedoch nur eine Datei mit einer Liste der verbundenen Dateien.

Also aus der Kommandozeilenperspektive:

$ cp dir1 copyDir1

Würde im Grunde bedeuten, die genannte Datei dir1in eine neue Datei mit dem Namen zu kopieren copyDir1. Und was das Dateisystem betrifft, dir1ist es sowieso nur eine Datei; Die Tatsache, dass es sich um ein „Verzeichnis“ handelt, wird nur sichtbar, wenn das Dateisystem tatsächlich prüft dir1, um festzustellen, was dieser Stapel von Bits tatsächlich ist.

Das -rFlag weist das Dateisystem an, den Datei- / Verzeichnisbaum rekursiv zu rollen und alle Inhalte, die ein „Kind“ dieser Datei sein könnten, an einen neuen Ort zu kopieren.

Nun, warum das überflüssig oder überflüssig erscheint, hängt es wirklich mit historischen Methoden des Umgangs mit Dateisystemen zusammen. Sowie die Schaffung eines Systems, das vor allen Arten von benutzerbezogenen Fehlern sicher ist; zufällig sowie absichtlich.

Das heißt, sagen wir , Sie eine haben ~/binDatei in Ihrem Home - Verzeichnis , das Sie kopieren möchten , aber die versehentlich ausgelassen ~-weil Sie ein Mensch und machen Fehler-so sein gerade sind /binwie folgt aus :

cp /bin/ ~/copy_of_bin

Mit dem „Sicherheitsnetz“ /bin, ein Verzeichnis zu sein, verbunden mit der Notwendigkeit des -rFlags, vermeiden Sie, dass Sie versehentlich die gesamte Binärwurzel des Systems, auf dem Sie sich befinden, in Ihr Home-Verzeichnis kopieren. Wenn dieses Sicherheitsnetz nicht vorhanden wäre, würde eine kleine - oder möglicherweise große - Katastrophe eintreten.

Die Logik hier ist, dass in den Tagen vor der GUI (grafische Benutzeroberflächen) logische / Verhaltens-Konventionen festgelegt werden müssen, um zu vermeiden, dass der Benutzer Pannen verursacht, die ein System möglicherweise töten können. Und die -rFlagge zu benutzen ist jetzt eine davon.

Wenn das überflüssig erscheint, brauchen Sie nicht weiter zu suchen als mit einem modernen GUI-System, das man über Linux-Dateisystemen platzieren kann. Eine grafische Benutzeroberfläche behebt grundlegende Benutzerprobleme wie diese, indem sie das einfache Ziehen und Ablegen von Dateien und Verzeichnissen ermöglicht.

Aber im Bereich der textbasierten Benutzeroberflächen ist ein Großteil der „Benutzererfahrung“ in dieser Welt im Grunde genommen nur logische und hueristische Unebenheiten, die dazu beitragen, den Benutzer in Schach zu halten, damit potenzielle Katastrophen abgewendet werden können.

Auf ähnliche Weise haben Linux / Unix-Dateisysteme standardmäßig keine 777Berechtigungen und sudoRechte festgelegt und wie echte Systemadministratoren zusammenzucken, wenn ein Benutzer 777Berechtigungen festlegt oder jedem sudoRechte gewährt . Dies sind die grundlegenden Dinge, die man tut, um sicherzustellen, dass das System stabil und so benutzerfreundlich wie möglich ist. Wer sich beeilt, diese Konventionen kurzzuschließen, wird höchstwahrscheinlich sein System beschädigen, ohne es überhaupt zu wissen.

ZUSÄTZLICHE INFORMATIONEN: Eine andere Antwort hier auf der Unix Stack Exchange-Website gibt eine gute Erklärung dafür, warum eine nicht rekursive Kopie eines Verzeichnisses problematisch ist. Betonung liegt bei mir.

Nun, ohne das -R-Flag ist es nur möglich, Dateien zu kopieren, da es eher ungewöhnlich ist, dass jemand ein Verzeichnis nicht rekursiv kopieren möchte : Eine nicht rekursive Kopie würde nur einen zweiten Namen für das Verzeichnis ergeben, der direkt auf das Verzeichnis verweist gleiche Verzeichnisstruktur. Da dies nur selten gewünscht wird und es tatsächlich ein separates Programm gibt, das dies ausführt (ln), ist eine nicht rekursive Kopie von Verzeichnissen nicht zulässig.

Wenn ein Verzeichnis also nur eine Datei mit Inode-Elementen ist, entspricht das Erstellen einer direkten Kopie dieser Datei der Funktionsweise einer festen Verknüpfung. Welches ist nicht das, was jemand will.

JakeGould
quelle
19
Ich persönlich denke, dass der "Schutz" -Aspekt den Geruchstest nicht besteht. Manche Menschen könnten genauso leicht tippen cp -r /binwie cp-r ~/bin. Die Flagge selbst verhindert nicht, dass Fehler gemacht werden oder dass jemand besser aufpasst. Wenn Sie Fehler vermeiden möchten, kann der Befehl cp den betreffenden Knoten genauso einfach anzeigen und eine Eingabeaufforderung wie "Dies ist ein Verzeichnis, möchten Sie den gesamten Inhalt an den angegebenen Speicherort kopieren (y / n)? Das wäre ein Sicherheitsnetz . Durch das Erfordernis von -r für Verzeichnisse bleibt der Code mehr als alles andere aufgebläht.
JDL
12
Schlechte Analogie zum Mannloch. Wie @JDL bereits sagte, verhindert das betreffende Flag keine Tippfehler im Pfad. Ich wäre froh, wenn ich die Übereinstimmung mit anderen Befehlen als Grund akzeptieren würde, aber ich habe das Gefühl, der wahre Grund ist "so wurde es zuerst geschrieben und jetzt hängen so viele Dinge von diesem Verhalten ab, es ist unmöglich, es zu ändern".
Grund
7
Wenn wir bewegen ein Verzeichnis haben wir nicht brauchen -r. Ich denke, dass die verknüpfte Antwort auf unix.stackexchange.com viel aussagekräftiger ist. Das Äquivalent einer nicht rekursiven Kopie wäre, ein zweites Verzeichnis und feste Verknüpfungen für alle Dateien in der Verzeichnisstruktur zu haben.
Gerrit
2
Wenn mich nicht alles täuscht, -r war eine GNU - Erweiterung - ich glaube nicht , das historische UNIX cp hatte eine rekursive Kopie - und das war ein Teil des Grundes für den Befehl rsync .
Mei
2
@JakeGould Ich verstehe grundlegende Konzepte. Ich widerspreche jedoch der Vorstellung, dass ein -r-Flag auf dem fraglichen Befehl zusätzliche Sicherheit bietet. Nach meiner Erfahrung sind Kommandozeilen-Linux-Kommandos von Natur aus unsicher. Wenn überhaupt, wurde die Sicherheit erhöht. Die dem Linux-Design innewohnende Sicherheit beruht auf dem Berechtigungssystem und dem Verzicht auf die Ausführung als Root. Nicht als root zu laufen ist auch eher eine Konvention als ein Design. Die Konvention wird in den meisten neuen Linux-Installationsprogrammen unterstützt, war es aber nicht immer.
JDL
19

Es ist sehr richtig, dass wir dieses Verhalten fast die ganze Zeit wollen. Dies bedeutet jedoch nicht unbedingt, dass das rekursive Kopieren das Standardverhalten sein sollte.

Ich denke, die Gründe cpdafür liegen in der Unix-Philosophie . Unix bevorzugt Programme, die eine Sache tun und es gut machen , sowie Programme, die sowohl in der Oberfläche als auch in der Implementierung einfach sind (manchmal als schlechter ist besser bezeichnet ).

Das Schlüsselstück des Puzzles ist die Erkenntnis, dass cpkeine Verzeichnisse cpkopiert werden - es werden Dateien (und nur Dateien) kopiert . Wenn Sie ein Verzeichnis kopieren möchten, cp rufen Sie sich rekursiv auf , um die Dateien in jedes Verzeichnis zu kopieren.

Natürlich ist der Unterschied zwischen dem "Kopieren von Verzeichnissen" und dem "rekursiven Kopieren von Dateien" aus Benutzersicht absolut nichts, aber diese Schnittstelle hilft bei der Implementierung, einfach zu bleiben .

Wenn Sie cpVerzeichnisse kopieren könnten, wären Sie bald versucht, weitere Funktionen hinzuzufügen, die nur für Verzeichnisse sinnvoll sind. Sie möchten beispielsweise möglicherweise nur Dateinamen kopieren, die auf enden .sh. Dies führt unweigerlich zu dem Aufblähen und Funktionsschleichen , das wir von anderen Betriebssystemen gewohnt sind - was Software langsam, komplex und fehleranfällig macht.

Ein weiterer Vorteil ist, dass -rder Benutzer auch besser verstehen kann, was unter der Benutzeroberfläche wirklich passiert. Ein netter Nebeneffekt davon ist, dass das Erlernen des Konzepts der rekursiven Operation Ihnen einige Arbeit erspart, wenn Sie mehr über andere Tools erfahren, die dies unterstützen (wie grepzum Beispiel).


Einige Leute werden Sie sicher sagen , dass Implementierungsdetails für den Benutzer auszusetzen ist schlecht , und dass mehr Funktionen aufweist , ist gut . Meine Absicht hier ist nur, die Gründe für dieses Verhalten zu erklären, also werde ich nicht versuchen, so oder so zu argumentieren.

Goncalopp
quelle
2
+1 „… mach eine Sache und mach es gut…“ Danke, dass du das angegeben hast!
JakeGould
5

Durch die Interaktion mit Verzeichnissen wird sichergestellt, dass Sie mit einem Verzeichnis und NICHT nur mit einer einzelnen Datei interagieren.

Zum Beispiel:

$ 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

Wenn Sie also einen Ordner erfolgreich kopieren möchten, müssen Sie ihn so behandeln, als wäre er eine Sammlung von Dateien:

$ cp -r folder1/ folder2
$ rm -rf folder1
mbb
quelle
3

Das Ändern der Standardeinstellung hätte zur Folge, dass Tausende von Shell-Skripten nicht mehr funktionieren. Dies führt zu den POSIX- und SUS-Anforderungen für das bekannte Standardverhalten.

Der Grund ist die historische Entwicklung der Befehle cp, ln und mv (auf den meisten alten UNIX-Systemen alle gleich binär) in verschiedenen UNIX-Zweigen. Als es -rerschien (Early cphatte keine Möglichkeit, Verzeichnisse zu kopieren; hier ist eine frühe Manpage ohne -roder -R), gab es verschiedene Unterschiede im Umgang mit speziellen Dateien, Symlinks und anderen Unklarheiten des Dateisystems.

Aus den Open Group Base-Spezifikationen, Ausgabe 7 :

Frühere Versionen dieses Standards unterstützten die Option -r zum Kopieren von Dateihierarchien. Die Option -r ist in der Vergangenheit bei BSD und von BSD abgeleiteten Systemen üblich. Diese Option wird von POSIX.1-2008 nicht mehr angegeben, ist jedoch möglicherweise in einigen Implementierungen vorhanden. Die Option -R wurde als nahes Synonym zu der Option -r hinzugefügt, die aus Gründen der Konsistenz mit allen anderen Optionen in diesem Volume von POSIX.1-2008 ausgewählt wurde, die eine rekursive Verzeichnisherabsetzung durchführen.

Der Unterschied zwischen -R und der entfernten Option -r besteht darin, dass andere Dateitypen als regular und directory nach cp behandelt werden. Es wurde durch die Implementierung festgelegt, wie die Option - spezielle Dateien behandelt, um sowohl historische Implementierungen als auch diejenigen, die -r unterstützen, mit den gleichen Fähigkeiten wie -R zu ermöglichen, die in diesem Band von POSIX.1-2008 definiert sind. Das ursprüngliche -r-Flag behandelte aus historischen Gründen keine speziellen Dateien anders als reguläre Dateien, sondern las die Datei immer und kopierte ihren Inhalt. Dies hatte offensichtliche Probleme beim Vorhandensein spezieller Dateitypen; Zum Beispiel Zeichengeräte, FIFOs und Sockets.

Tatsächlich werden Sie immer noch einige Leute sehen, die regelmäßig Folgendes verwenden:

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

Weil sie nicht darauf vertrauen, dass die cp -rImplementierung so ist, wie sie es von einer beliebigen Maschine gewohnt ist. Oder weil sie das tarVerhalten wollen .

kael
quelle
3

Es mag heute eine suboptimale Benutzeroberfläche sein, aber es war eine Entscheidung, die irgendwann um 1970 während des Entwurfs von UNIX getroffen wurde, als die Festplatte erheblich teurer war. Millionen von Shell-Skripten hängen davon ab, dass sie auf diese Weise funktionieren, und es ist viel zu spät, um sie zu ändern.

In diesem Artikel finden Sie Informationen zum Originaldesign.

pjc50
quelle
3

Ein klarer Vorteil des -rFlags ist, dass Sie cp * /target/dirnur alle Dateien im Quellverzeichnis in das Zielverzeichnis kopieren können , wobei (wenn auch mit einer Warnung) alle darin enthaltenen Verzeichnisse weggelassen werden. cp -r * /target/dirwürde stattdessen alles kopieren, einschließlich der Unterverzeichnisse.

Interneci
quelle
2

Sie benötigen dieses Flag nur, wenn cpes sich um einen Befehl zum Kopieren von Dateien und Verzeichnissen handelt und nicht nur von Verzeichnissen.

Wenn es einen speziellen Befehl für das Kopieren von Verzeichnissen gäbe, wäre das "Standard" -Verhalten sicherlich eine rekursive Kopie.

Kumpel
quelle
1
Macht Sinn. Aber warum sollte jemand ein Verzeichnis kopieren, das nicht mindestens eine Datei enthält? Warum dann nicht einfach nutzen mkdir?
JakeGould
1
@JakeGould vielleicht, weil sie Besitzrechte und Berechtigungen bewahren müssen?
Ruslan
1

Wie bereits erwähnt, handelt es sich bei einem Verzeichnis im Grunde genommen nur um einen anderen Dateityp (im Gegensatz zu einer regulären Datei), der normalerweise andere Dateien "enthält" (auf diese verweist). Es kann Unterverzeichnisse enthalten, für die das Gleiche gilt ...

Wenn Sie also ein Verzeichnis kopieren (Benutzerperspektive), kopieren Sie tatsächlich eine Reihe von Dateien (Dateisystemperspektive) (reguläre Dateien, Verzeichnisdateien, symbolische Links, ...) und wiederholen dies für jede Verzeichnisdatei rekursiv Prozess. Da das Kopieren eines Verzeichnisses per Definition ein rekursiver Prozess ist, wird das Argument von cp aufgerufen --recursive.

Es ist natürlich sehr einfach, eine Befehlsverknüpfung in Ihrer Benutzerumgebung zu erstellen (fügen Sie diese in Ihre .profile / .bashrc-Datei ein, um sie dauerhaft verfügbar zu machen):

alias cpr='cp -r'

Oder vielleicht besser:

alias cpa='cp -av'

Auf diese Weise können Sie ein Verzeichnis mit kopieren cpa dir1 copyDir1und es wird nicht nur gedruckt, was kopiert wird, sondern auch Dateiberechtigungen angewendet.

Und da jemand erwähnt hat, dass cp theoretisch erkennen könnte, dass die Quelldatei ein Verzeichnis ist, und fragt, ob sie rekursiv kopiert werden soll, hier ein kurzer Vorschlag:

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 "$@"
}

Dies ist nur ein billiger CP-Wrapper. Es werden immer alle Metadaten beibehalten (dh die Änderungszeit der Datei wird kopiert, die Symlinks werden ordnungsgemäß kopiert usw.). Wenn Sie versuchen, ein Verzeichnis zu kopieren, werden Sie gefragt, ob es (rekursiv) kopiert werden soll.

basic6
quelle