Gestern habe ich diesen SO-Kommentar gelesen , der besagt, dass in der Shell (zumindest bash
) >&-
"dasselbe Ergebnis hat wie" >/dev/null
.
Dieser Kommentar bezieht sich tatsächlich auf den ABS-Leitfaden als Informationsquelle. Diese Quelle besagt jedoch, dass die >&-
Syntax "Dateideskriptoren schließt".
Mir ist nicht klar, ob die beiden Aktionen, einen Dateideskriptor zu schließen und ihn auf das Null-Gerät umzuleiten, völlig gleichwertig sind. Meine Frage ist also: Sind sie?
Auf den ersten Blick scheint das Schließen eines Deskriptors wie das Schließen einer Tür zu sein, aber das Umleiten auf ein Null-Gerät öffnet eine Tür in die Schwebe! Die beiden scheinen mir nicht genau gleich zu sein, denn wenn ich eine geschlossene Tür sehe, werde ich nicht versuchen, etwas herauszuwerfen, aber wenn ich eine offene Tür sehe, gehe ich davon aus, dass ich es kann.
Mit anderen Worten, ich habe mich immer gefragt, ob das >/dev/null
bedeutet, dass cat mybigfile >/dev/null
tatsächlich jedes Byte der Datei verarbeitet und in die Datei geschrieben wird, /dev/null
die es vergisst. Auf der anderen Seite, wenn die Shell auf einen geschlossenen Dateideskriptor stößt, glaube ich (bin mir aber nicht sicher), dass sie einfach nichts schreibt, obwohl die Frage bleibt, ob cat
immer noch jedes Byte gelesen wird.
Dieser Kommentar sagt >&-
und >/dev/null
" sollte " gleich sein, aber es ist keine so durchschlagende Antwort auf mich. Ich hätte gerne eine verbindlichere Antwort mit einem Verweis auf den Standard- oder Quellkern oder nicht ...
quelle
Antworten:
Nein, Sie möchten die Dateideskriptoren 0, 1 und 2 auf keinen Fall schließen.
Wenn Sie dies tun, wird die Datei beim ersten Öffnen der Anwendung zu stdin / stdout / stderr ...
Zum Beispiel, wenn Sie:
Wenn
tee
(zumindest einige Implementierungen, wie z. B. busybox ') die Datei zum Schreiben öffnet, wird sie in Dateideskriptor 1 (stdout) geöffnet. Alsotee
schreibetext
zweimal infile
:Es ist bekannt, dass dies Sicherheitslücken verursacht. Zum Beispiel:
Und
chsh
(eine Setuid-Anwendung) kann dazu führen, dass Fehlermeldungen in geschrieben werden/etc/passwd
.Einige Tools und sogar einige Bibliotheken versuchen, sich dagegen zu schützen. Zum Beispiel verschiebt GNU
tee
den Dateideskriptor auf einen Wert über 2, wenn die Dateien, die zum Schreiben geöffnet werden, mit 0, 1, 2 belegt sind, während busybox diestee
nicht tut .Die meisten Tools melden, wenn sie nicht in stdout schreiben können (weil es beispielsweise nicht geöffnet ist), eine Fehlermeldung in stderr (in der Sprache des Benutzers, was eine zusätzliche Verarbeitung zum Öffnen und Parsen von Lokalisierungsdateien bedeutet ...) Dies ist erheblich weniger effizient und kann möglicherweise zum Fehlschlagen des Programms führen.
Effizienter geht es auf keinen Fall. Das Programm führt weiterhin einen
write()
Systemaufruf aus. Effizienter kann es nur sein, wenn das Programm nach dem ersten fehlgeschlagenenwrite()
Systemaufruf das Schreiben an stdout / stderr aufgibt , aber das tun Programme im Allgemeinen nicht. Sie werden in der Regel entweder mit einem Fehler beendet oder versuchen es erneut.quelle
Es ist keine vollständige Antwort auf Ihre Frage, aber so funktioniert es.
cat
Liest die benannte (n) Datei (en) oder Standardeingabe (n), wenn keine Dateien benannt sind, und gibt deren Inhalt an die Standardausgabe aus, bis ein EOF (einschließlich Standardeingabe) für die zuletzt benannte Datei auftritt. Das ist seine Aufgabe.Durch das Hinzufügen
>/dev/null
leiten Sie die Standardausgabe nach / dev / null um. Dies ist eine spezielle Datei (ein Geräteknoten), die alles, was darin geschrieben ist, wegwirft (und beim Lesen sofort EOF zurückgibt). Beachten Sie, dass die E / A-Umleitung von der Shell und nicht von jeder einzelnen Anwendung bereitgestellt wird und dass der Name / dev / null nichts Magisches ist, sondern nur das, was dort auf den meisten Unix-ähnlichen Systemen vorkommt .Es ist auch wichtig zu beachten, dass die spezifischen Mechanismen der Geräteknoten von Betriebssystem zu Betriebssystem unterschiedlich sind, cat (was in einem GNU-System Coreutils bedeutet) jedoch plattformübergreifend ist (derselbe Quellcode muss mindestens unter Linux und ausgeführt werden) Hurd) und kann daher keine Abhängigkeiten zu bestimmten Betriebssystemkernen übernehmen. Darüber hinaus funktioniert es weiterhin, wenn Sie einen / dev / null-Alias (unter Linux bedeutet dies einen Geräteknoten mit derselben Haupt- / Nebengerätenummer) mit einem anderen Namen erstellen. Und es gibt immer den Fall, dass an einer anderen Stelle geschrieben wird, die sich praktisch gleich verhält (z. B. / dev / zero).
Daraus folgt, dass
cat
die besonderen Eigenschaften von / dev / null nicht bekannt sind und dass die Umleitung wahrscheinlich überhaupt nicht bekannt ist, dass sie jedoch genau die gleiche Arbeit leisten muss: Sie liest die genannten Dateien und gibt den Inhalt von aus die / jene Datei (en) auf ihre Standardausgabe. Dass die Standardausgabe voncat
zufällig ins Leere geht, ist an sich nichts,cat
worum es geht.quelle
cat mybigfile > /dev/null
wird dazu führencat
, dass jedes Bytebigfile
in den Speicher gelesen wird . Und für jedesn
Byte wird es aufgerufenwrite(1, buffer, n)
. Unbekannt für dascat
Programm,write
wird der absolut nichts tun (außer vielleicht für einige triviale Buchhaltung). Das Schreiben in/dev/null
erfordert nicht die Verarbeitung jedes Bytes.cat
odercp
das würde funktionieren, indemmmap
große Teile der Quelldatei in den Speicher geschrieben und dannwrite()
die zugeordnete Region aufgerufen werden. Wenn Sie schreiben/dev/null
,write()
kehrt der Aufruf sofort zurück, ohne die Seiten der Quelldatei zu beschädigen, sodass er niemals tatsächlich von der Festplatte gelesen wird.cat
läuft auf vielen Plattformen, aber ein gelegentlicher Blick auf den Quellcode zeigt viele#ifdef
s: Es ist nicht buchstäblich derselbe Code, der auf allen Plattformen läuft, und es gibt viele systemabhängige Abschnitte.