In den überraschend häufigen Fällen, in denen Sie tatsächlich alle nicht leeren Zeilen innerhalb einer Variablen auf irgendeine Weise verarbeiten müssen (einschließlich deren Zählung), können Sie IFS auf eine neue Zeile setzen und dann den Wortaufteilungsmechanismus der Shell verwenden, um zu brechen die nicht leeren Zeilen auseinander.
Hier ist zum Beispiel eine kleine Shell-Funktion, die die nicht leeren Zeilen in allen angegebenen Argumenten summiert:
lines() (
IFS='
'
set -f #disable pathname expansion
set -- $*
echo $#
)
Hier werden Klammern anstelle von geschweiften Klammern verwendet, um den zusammengesetzten Befehl für den Funktionskörper zu bilden. Dadurch wird die Funktion in einer Subshell ausgeführt, sodass die IFS-Variablen- und Pfadnamen-Erweiterungseinstellung der Außenwelt nicht bei jedem Aufruf verschmutzt wird.
Wenn Sie über nicht leere Zeilen iterieren möchten, können Sie dies auf ähnliche Weise tun:
IFS='
'
set -f
for line in $lines
do
printf '[%s]\n' $line
done
Das Manipulieren von IFS auf diese Weise ist eine häufig übersehene Technik, die sich auch zum Parsen von Pfadnamen eignet, die Leerzeichen aus tabulatorgetrennten Spalteneingaben enthalten können. Sie müssen sich jedoch darüber im Klaren sein, dass das absichtliche Entfernen des Leerzeichens, das normalerweise in der IFS-Standardeinstellung für Leerzeichen-Tabulator-Zeilenumbruch enthalten ist, dazu führen kann, dass die Wortaufteilung an Stellen deaktiviert wird, an denen Sie sie normalerweise erwarten würden.
Wenn Sie beispielsweise Variablen verwenden, um eine komplizierte Befehlszeile für etwas wie zu erstellen ffmpeg
, möchten Sie diese möglicherweise -vf scale=$scale
nur einschließen , wenn die Variable scale
auf etwas nicht Leeres festgelegt ist. Normalerweise mit Ihnen könnte dies zu erreichen , ${scale:+-vf scale=$scale}
aber wenn IFS nicht seinen üblichen Raumzeichen zum Zeitpunkt dieser Parameter Expansion erfolgt sind, zwischen der Raum -vf
und scale=
wird nicht als Worttrennzeichen verwendet werden und ffmpeg
wird alle weitergegeben werden -vf scale=$scale
als ein einziges Argument, was es nicht verstehen wird.
Um dies zu beheben, müssen Sie entweder sicherstellen, dass IFS normaler eingestellt wurde, bevor Sie die ${scale}
Erweiterung durchführen, oder zwei Erweiterungen durchführen : ${scale:+-vf} ${scale:+scale=$scale}
. Die Wortaufteilung, die die Shell beim anfänglichen Parsen von Befehlszeilen durchführt, im Gegensatz zur Aufteilung während der Erweiterungsphase der Verarbeitung dieser Befehlszeilen, hängt nicht von IFS ab.
Etwas anderes, das sich lohnen könnte, wenn Sie so etwas tun, wäre, zwei globale Shell-Variablen zu erstellen, die nur einen Tabulator und nur eine neue Zeile enthalten:
t=' '
n='
'
Auf diese Weise können Sie einfach schließen $t
und $n
in Erweiterungen , wo Sie Tabs und Zeilenumbrüche müssen, anstatt Littering den gesamten Code mit zitierte Leerzeichen. Wenn Sie in einer POSIX-Shell, für die es keinen anderen Mechanismus gibt, lieber Leerzeichen in Anführungszeichen vermeiden printf
möchten , kann dies hilfreich sein, obwohl Sie ein wenig herumfummeln müssen, um das Entfernen von nachgestellten Zeilenumbrüchen in Befehlserweiterungen zu umgehen:
nt=$(printf '\n\t')
n=${nt%?}
t=${nt#?}
Manchmal funktioniert es gut, IFS so einzustellen, als wäre es eine Umgebungsvariable pro Befehl. Hier ist beispielsweise eine Schleife, die einen Pfadnamen liest, der Leerzeichen und einen Skalierungsfaktor aus jeder Zeile einer durch Tabulatoren getrennten Eingabedatei enthalten darf:
while IFS=$t read -r path scale
do
ffmpeg -i "$path" ${scale:+-vf scale=$scale} "${path%.*}.out.mkv"
done <recode-queue.txt
In diesem Fall wird read
IFS nur auf eine Registerkarte gesetzt, sodass die Eingabezeile, die es liest, nicht auch auf Leerzeichen aufgeteilt wird. Funktioniert IFS=$t set -- $lines
jedoch nicht : Die Shell wird erweitert $lines
, set
wenn die Argumente des eingebauten Geräts vor dem Ausführen des Befehls erstellt werden. Daher kommt die temporäre Einstellung von IFS auf eine Weise, die nur während der Ausführung des integrierten Geräts selbst gilt, zu spät. Aus diesem Grund setzen die Code-Schnipsel, die ich vor allem gegeben habe, IFS in einem separaten Schritt und müssen sich mit dem Problem der Aufbewahrung befassen.
wc -l
entspricht genau dem Original:<<<$foo
Fügt dem Wert von eine neue Zeile hinzu$foo
(auch wenn diese$foo
leer war). Ich erkläre in meiner Antwort, warum dies möglicherweise nicht das war, was gewünscht wurde, aber es wurde gefragt.