it-swarm.com.de

Warum wurde "cp" entwickelt, um vorhandene Dateien stillschweigend zu überschreiben?

Ich habe cp mit den folgenden Befehlen getestet:

$ ls
first.html   second.html  third.html

$ cat first.html
first

$ cat second.html
second

$ cat third.html
third

Dann kopiere ich first.html bis second.html:

$ cp first.html second.html

$ cat second.html
first

Die Datei second.html wird ohne Fehler stillschweigend überschrieben. Wenn ich dies jedoch in einer Desktop-GUI durch Ziehen und Ablegen einer Datei mit demselben Namen mache, wird das Suffix first1.html automatisch. Dadurch wird vermieden, dass eine vorhandene Datei versehentlich überschrieben wird.

Warum folgt cp nicht diesem Muster, anstatt Dateien stillschweigend zu überschreiben?

29
Calculus

Das Standard-Überschreibverhalten von cp ist in POSIX angegeben.

  1. Wenn source_file vom Typ reguläre Datei ist, müssen die folgenden Schritte ausgeführt werden:

    3.a. Das Verhalten ist nicht angegeben, wenn dest_file vorhanden ist und von einem vorherigen Schritt geschrieben wurde. Andernfalls müssen, wenn dest_file vorhanden ist, die folgenden Schritte ausgeführt werden:

    3.a.i. Wenn die Option -i aktiviert ist, schreibt das Dienstprogramm cp eine Eingabeaufforderung für den Standardfehler und liest eine Zeile aus der Standardeingabe. Wenn die Antwort nicht positiv ist, wird cp mit source_file nichts mehr tun und mit den verbleibenden Dateien fortfahren.

    3.a.ii. Ein Dateideskriptor für dest_file muss erhalten werden, indem Aktionen ausgeführt werden, die der open () -Funktion entsprechen, die im System Interfaces-Volume von POSIX.1-2017 definiert ist, das dest_file als Pfadargument verwendet, und das bitweise inklusive OR von O_WRONLY und O_TRUNC als oflag-Argument.

    3.a.iii. Wenn der Versuch, einen Dateideskriptor abzurufen, fehlschlägt und die Option -f aktiviert ist, versucht cp, die Datei zu entfernen, indem Aktionen ausgeführt werden, die der Funktion unlink () entsprechen, die im Systemschnittstellen-Volume von POSIX.1-2017 definiert ist, das mit dest_file aufgerufen wird als Pfadargument. Wenn dieser Versuch erfolgreich ist, fährt cp mit Schritt 3b fort.

Als die POSIX-Spezifikation geschrieben wurde, gab es bereits eine große Anzahl von Skripten mit einer integrierten Annahme für das Standardüberschreibungsverhalten. Viele dieser Skripte wurden entwickelt, um ohne direkte Benutzerpräsenz ausgeführt zu werden, z. als Cron Jobs oder andere Hintergrundaufgaben. Eine Änderung des Verhaltens hätte sie gebrochen. Das Überprüfen und Ändern aller Elemente, um eine Option zum Erzwingen des Überschreibens bei Bedarf hinzuzufügen, wurde wahrscheinlich als große Aufgabe mit minimalen Vorteilen angesehen.

Außerdem wurde die Unix-Befehlszeile immer so konzipiert, dass ein erfahrener Benutzer effizient arbeiten kann, selbst auf Kosten einer harten Lernkurve für Anfänger. Wenn der Benutzer einen Befehl eingibt, muss der Computer erwarten, dass der Benutzer dies wirklich meint, ohne dass er darüber nachdenken muss. Es liegt in der Verantwortung des Benutzers, mit potenziell zerstörerischen Befehlen vorsichtig umzugehen.

Als das ursprüngliche Unix entwickelt wurde, hatten die Systeme im Vergleich zu modernen Computern so wenig Speicher und Massenspeicher, dass das Überschreiben von Warnungen und Eingabeaufforderungen wahrscheinlich als verschwenderischer und unnötiger Luxus angesehen wurde.

Als der POSIX-Standard geschrieben wurde, war der Präzedenzfall fest etabliert, und die Verfasser des Standards waren sich der Vorteile von bewusst, die Abwärtskompatibilität nicht zu brechen .

Außerdem kann, wie andere beschrieben haben, jeder Benutzer diese Funktionen für sich selbst hinzufügen/aktivieren, indem er Shell-Aliase verwendet oder sogar einen Ersatzbefehl cp erstellt und seinen $PATH Ändert, um den Ersatz vor dem zu finden Standard-Systembefehl, und erhalten Sie das Sicherheitsnetz auf diese Weise, falls gewünscht.

Wenn Sie dies jedoch tun, werden Sie feststellen, dass Sie eine Gefahr für sich selbst darstellen. Wenn sich der Befehl cp bei interaktiver Verwendung auf eine Weise und beim Aufruf aus einem Skript auf eine andere Weise verhält, können Sie sich möglicherweise nicht daran erinnern, dass der Unterschied besteht. Auf einem anderen System sind Sie möglicherweise nachlässig, weil Sie sich an die Warnungen und Eingabeaufforderungen auf Ihrem eigenen System gewöhnt haben.

Wenn das Verhalten in Skripten immer noch mit dem POSIX-Standard übereinstimmt, werden Sie sich wahrscheinlich an die Eingabeaufforderungen bei interaktiver Verwendung gewöhnen. Schreiben Sie dann ein Skript, das Massenkopien ausführt, und stellen Sie dann fest, dass Sie versehentlich etwas überschrieben haben.

Wenn Sie die Eingabeaufforderung auch in Skripten erzwingen, was bewirkt der Befehl, wenn er in einem Kontext ausgeführt wird, in dem sich kein Benutzer befindet, z. Hintergrundprozesse oder Cron Jobs? Wird das Skript hängen bleiben, abbrechen oder überschreiben?

Hängen oder Abbrechen bedeutet, dass eine Aufgabe, die automatisch erledigt werden sollte, nicht erledigt wird. Das Nichtüberschreiben kann manchmal auch selbst ein Problem verursachen: Beispielsweise können alte Daten zweimal von einem anderen System verarbeitet werden, anstatt durch aktuelle Daten ersetzt zu werden.

Ein großer Teil der Leistungsfähigkeit der Befehlszeile beruht auf der Tatsache, dass Sie, sobald Sie wissen, wie etwas in der Befehlszeile ausgeführt wird, implizit auch wissen, wie dies durch Skripten automatisch geschehen kann . Dies gilt jedoch nur, wenn die Befehle, die Sie interaktiv verwenden, auch beim Aufrufen in einem Skriptkontext genauso funktionieren. Alle signifikanten Verhaltensunterschiede zwischen interaktiver Verwendung und Verwendung mit Skripten führen zu einer Art kognitiver Dissonanz, die einen Hauptbenutzer stört.

51
telcoM

cp stammt aus dem Anfang von Unix. Es war lange bevor der Posix-Standard geschrieben wurde. In der Tat: Posix hat gerade das bestehende Verhalten von cp in dieser Hinsicht formalisiert.

Wir sprechen über Epoche (1970-01-01), als Männer echte Männer waren, Frauen echte Frauen und pelzige kleine Kreaturen ... (ich schweife ab). In jenen Tagen wurde durch das Hinzufügen von zusätzlichem Code ein Programm größer. Das war damals ein Problem, denn der erste Computer, auf dem Unix lief, war ein PDP-7 (aufrüstbar auf 144 KB RAM!). Die Dinge waren also klein, effizient und ohne Sicherheitsmerkmale.

In jenen Tagen mussten Sie also wissen, was Sie taten, weil der Computer einfach nicht die Kraft hatte, Sie daran zu hindern, etwas zu tun, was Sie später bereuten.

(Es gibt einen schönen Cartoon von Zevar; suchen Sie nach "zevar cerveaux assiste par ordinateur", um die Entwicklung des Computers zu finden. Oder versuchen Sie es mit http://a54.idata.over-blog.com/2/07/). 74/62/dessins-et-bd/le-CAO-de-Zevar --- reduc.jpg solange es existiert)

Für diejenigen, die wirklich interessiert sind (ich habe einige Spekulationen in den Kommentaren gesehen): Das ursprüngliche cp auf dem ersten Unix bestand aus zwei Seiten Assembler-Code (C kam später). Der relevante Teil war:

sys open; name1: 0; 0   " Open the input file
spa
  jmp error         " File open error
lac o17         " Why load 15 (017) into AC?
sys creat; name2: 0     " Create the output file
spa
  jmp error         " File create error

(Also, ein hartes sys creat)

Und wenn wir schon dabei sind: Version 2 von Unix verwendet (Code-Sniplet)

mode = buf[2] & 037;
if((fnew = creat(argv[2],mode)) < 0){
    stat(argv[2], buf);

das ist auch ein hartes creat ohne Tests oder Schutzmaßnahmen. Beachten Sie, dass der C-Code für V2 Unix von cp weniger als 55 Zeilen beträgt!

19
Ljm Dullaart

Weil diese Befehle auch für die Verwendung in Skripten gedacht sind, die möglicherweise ohne menschliche Aufsicht ausgeführt werden, und weil es viele Fälle gibt, in denen Sie das Ziel tatsächlich überschreiben möchten (die Philosophie der Linux-Shells lautet, dass der Mensch weiß, was Sie macht)

Es gibt noch einige Sicherheitsvorkehrungen:

  • GNU cp hat ein -n | --no-clobber Möglichkeit
  • wenn Sie mehrere Dateien in eine einzige kopieren, beschwert sich cp, dass die letzte kein Verzeichnis ist.
17
xenoid

Ist es "eins auf einmal tun"?

Dieser Kommentar klingt wie eine Frage zu einem allgemeinen Gestaltungsprinzip. Oft sind Fragen dazu sehr subjektiv und wir können keine richtige Antwort schreiben. Seien Sie gewarnt, dass wir in diesem Fall Fragen schließen können.

Manchmal haben wir eine Erklärung für die ursprüngliche Designauswahl, weil die Entwickler darüber geschrieben haben. Aber ich habe keine so schöne Antwort auf diese Frage.

Warum ist cp so gestaltet?

Das Problem ist, dass Unix über 40 Jahre alt ist.

Wenn Sie jetzt ein neues System erstellen, können Sie andere Designentscheidungen treffen. Das Ändern von Unix würde jedoch vorhandene Skripte beschädigen, wie in anderen Antworten erwähnt.

Warum wurde wascp entwickelt, um vorhandene Dateien stillschweigend zu überschreiben?

Die kurze Antwort lautet "Ich weiß nicht" :-).

Verstehe, dass cp nur ein Problem ist. Ich denke, keines der ursprünglichen Befehlsprogramme ist gegen das Überschreiben oder Löschen von Dateien geschützt. Die Shell hat ein ähnliches Problem beim Umleiten der Ausgabe:

$ cat first.html > second.html

Dieser Befehl überschreibt auch stillschweigend second.html.

Ich bin gespannt, wie all diese Programme neu gestaltet werden könnten. Dies kann zusätzliche Komplexität erfordern.

Ich denke, dies ist Teil der Erklärung: Frühes Unix betonte einfache Implementierungen. Für eine detailliertere Erklärung hierzu: siehe "schlechter ist besser", verlinkt am Ende dieser Antwort.

Sie können > second.html Ändern, sodass es mit einem Fehler beendet wird, wenn second.html Bereits vorhanden ist. Wie bereits erwähnt, möchte der Benutzer tut manchmal eine vorhandene Datei ersetzen. Zum Beispiel kann sie ein komplexes Kommando aufbauen und es mehrmals versuchen, bis es das tut, was sie will.

Der Benutzer kann zuerst rm second.html Ausführen, wenn dies erforderlich ist. Dies könnte ein guter Kompromiss sein! Es hat einige mögliche eigene Nachteile.

  1. Der Benutzer muss den Dateinamen zweimal eingeben.
  2. Die Leute bekommen auch große Probleme mit rm. Deshalb möchte ich rm auch sicherer machen. Aber wie? Wenn wir rm jeden Dateinamen anzeigen lassen und den Benutzer zur Bestätigung auffordern, muss er jetzt drei Befehlszeilen anstelle von einer schreiben. Wenn sie dies zu oft tun muss, wird sie es sich zur Gewohnheit machen und "y" eingeben, um dies ohne nachzudenken zu bestätigen. Es könnte also sehr nervig sein, und es könnte immer noch gefährlich sein.

Auf einem modernen System empfehle ich Installieren des Befehls trash und verwenden ihn nach Möglichkeit anstelle von rm. Die Einführung von Müllspeicher war eine großartige Idee, z. für einen grafischen Einzelbenutzer-PC .

Ich denke, es ist auch wichtig, die Einschränkungen der ursprünglichen Unix-Hardware zu verstehen - begrenzt RAM und Speicherplatz, Ausgabe auf langsamen Druckern sowie das System und die Entwicklung Software.

Beachten Sie, dass das ursprüngliche Unix nicht Tab-Vervollständigung hatte, um schnell einen Dateinamen für einen rm -Befehl einzugeben. (Außerdem hat die ursprüngliche Bourne-Shell keinen Befehlsverlauf, z. B. wenn Sie die Aufwärtspfeiltaste in bash verwenden.).

Bei der Druckerausgabe würden Sie den zeilenbasierten Editor ed verwenden. Dies ist schwieriger zu lernen als ein visueller Texteditor. Sie müssen einige aktuelle Zeilen drucken, entscheiden, wie Sie sie ändern möchten, und einen Bearbeitungsbefehl eingeben.

Die Verwendung von > second.html Ist ein bisschen wie die Verwendung eines Befehls in einem Zeileneditor. Der Effekt hängt vom aktuellen Status ab. (Wenn second.html Bereits vorhanden ist, wird der Inhalt verworfen.) Wenn der Benutzer sich über den aktuellen Status nicht sicher ist, wird erwartet, dass er zuerst ls oder ls second.html Ausführt.

"Einfache Implementierung" als Gestaltungsprinzip

Es gibt eine beliebte Interpretation des Unix-Designs, die beginnt:

Das Design muss sowohl in der Implementierung als auch in der Benutzeroberfläche einfach sein. Es ist wichtiger, dass die Implementierung einfach ist als die Schnittstelle. Einfachheit ist die wichtigste Überlegung in einem Design.

...

Gabriel argumentierte, dass "Schlimmer ist besser" eine erfolgreichere Software hervorbrachte als der MIT -Ansatz: Solange das ursprüngliche Programm grundsätzlich gut ist, wird die Implementierung anfangs viel weniger Zeit und Mühe kosten und wird es auch Die Portierung von Software auf neue Maschinen wird beispielsweise auf diese Weise viel einfacher. So wird sich ihre Verwendung schnell verbreiten, lange bevor ein [besseres] Programm entwickelt und bereitgestellt werden kann (First-Mover-Vorteil) ).

https://en.wikipedia.org/wiki/Worse_is_better

9
sourcejedi

Das Design von "cp" geht auf das ursprüngliche Design von Unix zurück. Tatsächlich steckte hinter dem Unix-Design eine kohärente Philosophie, die etwas weniger war als die, die halb im Scherz als Worse-is-Better bezeichnet wurde* *.

Die Grundidee ist, dass es einfach wichtiger ist, den Code einfach zu halten, als eine perfekte Benutzeroberfläche zu haben oder "das Richtige zu tun".

  • Einfachheit - Das Design muss sowohl in der Implementierung als auch in der Benutzeroberfläche einfach sein. Es ist wichtiger, dass die Implementierung einfach ist als die Schnittstelle . Einfachheit ist die wichtigste Überlegung in einem Design.

  • Korrektheit - Das Design muss in allen beobachtbaren Aspekten korrekt sein. Es ist etwas besser, einfach als richtig zu sein.

  • Konsistenz - Das Design darf nicht zu inkonsistent sein. In einigen Fällen kann die Konsistenz der Einfachheit halber geopfert werden. Es ist jedoch besser, die Teile des Entwurfs zu löschen, die sich mit weniger häufigen Umständen befassen , als eine der beiden Implementierungen einzuführen Komplexität oder Inkonsistenz.

  • Vollständigkeit - Das Design muss so viele wichtige Situationen abdecken, wie es praktisch ist. Alle vernünftigerweise erwarteten Fälle sollten abgedeckt werden. Die Vollständigkeit kann zugunsten jeder anderen Qualität geopfert werden. Tatsächlich muss die Vollständigkeit geopfert werden, wenn die Einfachheit der Implementierung gefährdet ist. Konsistenz kann geopfert werden, um Vollständigkeit zu erreichen, wenn die Einfachheit beibehalten wird. Besonders wertlos ist die Konsistenz der Schnittstelle.

( Hervorhebung meiner )

Wenn man sich daran erinnert, dass dies 1970 war, wäre der Anwendungsfall "Ich möchte diese Datei nur kopieren, wenn sie noch nicht existiert" ziemlich selten gewesen Anwendungsfall für jemanden, der eine Kopie ausführt. Wenn Sie das wollten, können Sie es vor der Kopie überprüfen, und das kann sogar per Skript erfolgen.

Der Autor des Aufsatzes hatte auch eine Theorie darüber, warum ein Betriebssystem mit diesem Entwurfsansatz dasjenige war, das sich gegen alle anderen Betriebssysteme durchgesetzt hat, die zu dieser Zeit gebaut wurden.

Ein weiterer Vorteil der schlechter-ist-besser-Philosophie besteht darin, dass der Programmierer darauf konditioniert ist, etwas Sicherheit, Komfort und Aufwand zu opfern, um eine gute Leistung und einen bescheidenen Ressourcenverbrauch zu erzielen. Programme, die mit dem New Jersey-Ansatz geschrieben wurden, funktionieren sowohl auf kleinen als auch auf großen Maschinen gut, und der Code ist portabel, da er auf einem Virus geschrieben ist.

Es ist wichtig zu bedenken, dass das ursprüngliche Virus grundsätzlich gut sein muss. In diesem Fall ist die Ausbreitung des Virus sichergestellt, solange es tragbar ist. Sobald sich das Virus verbreitet hat, wird es Druck geben, es zu verbessern, möglicherweise indem seine Funktionalität näher an 90% erhöht wird, aber die Benutzer wurden bereits darauf konditioniert, schlechter als das Richtige zu akzeptieren. Daher wird die schlechter-ist-besser-Software erstens Akzeptanz erlangen, zweitens die Benutzer dazu veranlassen, weniger zu erwarten, und drittens wird sie bis zu einem Punkt verbessert, der fast das Richtige ist.

* - oder was der Autor, aber sonst niemand, "The New Jersey Approach" nannte.

9
T.E.D.

Der Hauptgrund ist, dass eine GUI ist per Definition interaktiv, während eine Binärdatei wie /bin/cp Nur ein Programm ist, das von allen möglichen Orten aus aufgerufen werden kann, zum Beispiel von Ihrer GUI; - ). Ich wette, dass die überwiegende Mehrheit der Anrufe an /bin/cp Noch heute nicht von einem echten Terminal mit einem Benutzer erfolgt, der einen Shell-Befehl eingibt, sondern von einem HTTP-Server, einem Mail-System oder einem NAS. Ein eingebauter Schutz vor Benutzerfehlern ist in einer interaktiven Umgebung durchaus sinnvoll; weniger in einer einfachen Binärdatei. Zum Beispiel wird Ihre GUI höchstwahrscheinlich /bin/cp Im Hintergrund aufrufen, um die eigentlichen Vorgänge auszuführen, und müsste sich mit den Sicherheitsfragen zum Standardausgang befassen, obwohl sie nur den Benutzer gefragt hat!

Beachten Sie, dass es vom ersten Tag an fast trivial war, auf Wunsch einen sicheren Wrapper um /bin/cp Zu schreiben. Die * nix-Philosophie besteht darin, Benutzern einfache Bausteine ​​bereitzustellen: Von diesen ist /bin/cp Einer.