Bash-Skripting und Zitate

Bash-Skripting und Zitate

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 forSchleife 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 IFSist ''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 -print0Option 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 doBefehl 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.

verwandte Informationen