Ich lese gerade ein paar Bücher über Bash-Skripting und habe Mühe, die korrekte Verwendung von Anführungszeichen und Anführungszeichen zu verstehen IFS
. Vielleicht kann mir jemand mit einem kleinen Beispiel mit Dateinamen in Anführungszeichen helfen. Wenn ich das von einer Befehlszeile aus mache, funktioniert es, die Dateinamen richtig auszudrucken, auch wenn sie Leerzeichen enthalten:
set - *
for i in "$@"; do echo $i; done
Dies funktioniert nicht, da es bei Leerzeichen unterbrochen wird:
set - `find . -name "*"`
for i in "$@"; do echo $i; done
Das Gleiche gilt auch für:
IFS=$'\0' set - `find . -name "*" -print0`
for i in "$@"; do echo $i; done
Das Gleiche gilt für die Kombination, die IFS=$'\n'
und verwendet -print
. Warum schlagen diese alle fehl?
Das Folgende schlägt ebenfalls fehl, aber in diesem Fall tritt ein Fehler auf („bash: Syntaxfehler in der Nähe des unerwarteten Tokens ‚do‘“). Warum?
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
aber das hier funktioniert (beachten Sie das ";"):
IFS=$'\n'; for i in `find . -name "*" -type f`; do echo $i; done
und dies schlägt fehl, weil die Dateinamen überhaupt nicht aufgeteilt werden (die for
Schleife wird nur einmal ausgeführt):
IFS=''; for i in `find . -name "*" -type f -print0`; do echo -e "$i\n"; done
Also nochmals, warum scheitern der erste und dritte dort?
Und schließlich: Liege ich mit meiner Annahme richtig, dass beim Setzen von dasselbe IFS
ist ''
wie $'\0'
? (Ich habe im unmittelbar vorhergehenden Beispiel beides ausprobiert.) Wenn das so ist, warum brauche ich dann anscheinend $'\n'
anstatt nur \n
?
*Bash ist Version 4.3.42(1) in Ubuntu Gnome 16.04.
Antwort1
Beim Debuggen von Bash-Befehlen habe ich zwei Dinge gelernt: Erstens) Wenn es nicht so funktioniert, wie Sie es erwarten, geben Sie die Befehle per Echo/Printf aus, um sicherzustellen, dass sie so angezeigt werden, wie Sie es erwarten. Zweitens) Führen Sie die Befehle in Ihren Befehlen manuell aus, um zu sehen, ob sie ordnungsgemäß ausgeführt werden (es sei denn, es handelt sich um potenziell destruktive Befehle wie rm, dd, chmod 777 usw.).
Wie oder was genau versuchen Sie zu erreichen? Meins scheint mit diesem Befehl wörtlich einwandfrei zu funktionieren, Sie müssen also erklären, was Sie damit erreichen möchten. Haben Ihre Dateinamen Leerzeichen, die Probleme mit Ihrem Befehl verursachen? Solange Sie das Leerzeichen aus der IFS-Variable entfernen, funktioniert es wahrscheinlich wie gewünscht: IFS=$'\n\t'
. Die -print0
Option entfernt im Grunde alle Zeilentrennzeichen, sodass alles, was Sie von diesen Befehlen erwarten, überhaupt keine Trennzeichen hat, es sei denn, die Dateinamen enthalten Leerzeichen. Zu Ihrem zweiten Fragensatz:
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
Dieser Befehl wird nicht ordnungsgemäß ausgeführt, da Bash ihn folgendermaßen liest:
IFS=$'\n' for i in `find . -name "*" -type f`
do echo $i
done
Die erste Zeile setzt IFS auf: $'\n' for i in
. Danach lautet die nächste Zeile: do echo $i
, was nicht funktioniert, da der do
Befehl eine Schleife irgendeiner Art als vorhergehenden Befehl erfordert. Die nächste funktioniert, weil das Semikolon Bash mitteilt, dass das Ende des Befehls erreicht wurde, also:
IFS=$'\n'
for i in `find . -name "*" -type f`
do echo $i
done
Und schließlich: Liege ich mit meiner Annahme richtig, dass beim Festlegen von IFS „“ dasselbe ist wie „$'\0'“? (Ich habe im unmittelbar vorhergehenden Beispiel beides ausprobiert.) Wenn das so ist, warum brauche ich dann anscheinend „$'\n'“ und nicht nur „\n“?
Ja zum ersten Teil, ''
und '\0'
beide werden als NULL betrachtet, also sind sie im Grunde gleich. Das liegt daran, dass Bash $''
es als ANSI-C-String interpretiert. Und Sie müssen Anführungszeichen um das \n setzen, damit Bash es als C-String interpretiert, sodass ein Zeilenumbruchzeichen in das IFS eingefügt wird.
Weitere Informationen dazu finden Sie hierHier.