Ein üblicher Weg, Dinge mit ein paar Dateien zu tun, ist - und schlagen Sie mich nicht dafür:
for f in $(ls); do …
Um vor Dateien mit Leerzeichen oder anderen seltsamen Zeichen sicher zu sein, wäre ein naiver Weg:
find . -type f -print0 | while IFS= read -r -d '' file; …
Hier -d ''
steht kurz für das Einstellen des ASCII NUL wie in -d $'\0'
.
Aber warum ist das so? Warum sind ''
und $'\0'
das gleiche? Liegt das daran, dass die C-Wurzeln von Bash mit einer leeren Zeichenfolge immer nullterminiert sind?
for f in *
anstatt sie zu analysierenls
.for i in $(ls)
ist schrecklich dumm - ich schäme mich fast, dass ich es hier als schlechtes Beispiel verwendet habe.find … -exec
Dateien anstelle von Schleifen verwenden, was in den meisten Fällen funktioniert, in denen Sie stattdessen eine solche for-Schleife verwenden würden. Hierfind
kümmert sich alles um Sie.Antworten:
Das
man page of bash
liest:Da Zeichenfolgen normalerweise mit Null abgeschlossen sind, ist das erste Zeichen einer leeren Zeichenfolge das Nullbyte. - Für mich ergibt das Sinn. :) :)
Die Quelle lautet:
Für eine leere Zeichenfolge
delim
ist einfach das Null-Byte.quelle
split()
, wird sie zwischen den einzelnen Zeichen aufgeteilt. Ich vermute, dass ein "aus historischen Gründen" die beste Erklärung sein kann, die wir bekommen können.'\0'
mit'X\0'
Ihnen geben sollte'X\0'
, wenn es richtig gemacht wird. Dies hat nicht viel mit Funktionen auf hoher Ebene in Sprachen wie JavaScript @don zu tundelim = *list_optarg;
macht deutlich, warum es so ist.''
und$'\0'
das gleiche?", Michas gab die unmittelbare Erklärung von "das ist, was der Code tut". Ich skizzierte eine alternative Art des Umgangs mit der leeren Zeichenfolge, die ich als ebenso vernünftig ansah, und schlug vor, dass die Wahl der einen oder anderen nur eine Frage der Konvention oder des Zufalls sei.Es gibt zwei Mängel bei Bash, die sich gegenseitig ausgleichen.
Wenn Sie schreiben
$'\0'
, wird dies intern identisch mit der leeren Zeichenfolge behandelt. Beispielsweise:Dies liegt daran, dass intern bash alle Zeichenfolgen als C- Zeichenfolgen speichert , die nullterminiert sind. Ein Null- Byte markiert das Ende der Zeichenfolge. Bash schneidet die Zeichenfolge stillschweigend auf das erste Null-Byte ab (das nicht Teil der Zeichenfolge ist!).
Wenn Sie einen String als Argument an die
-d
Option desread
eingebauten Strings übergeben, betrachtet bash nur das erste Byte des Strings. Es wird jedoch nicht überprüft, ob die Zeichenfolge nicht leer ist. Intern wird eine leere Zeichenfolge als 1-Element-Byte-Array dargestellt, das nur ein Null-Byte enthält. Anstatt das erste Byte der Zeichenfolge zu lesen, liest bash dieses Null-Byte.read
Intern funktioniert die Maschinerie hinter dem eingebauten System dann gut mit Null-Bytes. Es liest Byte für Byte weiter, bis es das Trennzeichen findet.Andere Muscheln verhalten sich anders. Zum Beispiel ignorieren ash und ksh Nullbytes, wenn sie Eingaben lesen.
ksh -d ""
Liest mit ksh bis zu einem Zeilenumbruch. Shells sind so konzipiert, dass sie gut mit Text umgehen können, nicht mit Binärdaten. Zsh ist eine Ausnahme: Es wird eine Zeichenfolgendarstellung verwendet, die mit beliebigen Bytes, einschließlich Nullbytes, fertig wird. in zsh$'\0'
ist eine Zeichenfolge der Länge 1 (read -d ''
verhält sich aber seltsamerweise wieread -d $'\0'
).quelle
read
in Bash 4.3 geändert, sodass jetzt Null-Bytes übersprungen werden. Zum Beispielread x< <(printf a\\0a)
setztx
aufaa
statta
.