Warum muss beim Zurücksetzen von IFS auf Zeilenumbrüche eine Rücktaste eingefügt werden?

70

Ich bin gespannt, warum die Rücktaste erforderlich ist, wenn IFS so eingestellt wird, dass Zeilenumbrüche wie folgt aufgeteilt werden:

IFS=$(echo -en "\n\b")

Warum kann ich das nicht einfach verwenden (was nicht funktioniert)?

IFS=$(echo -en "\n")

Ich bin auf einem Linux-System, das Dateien mit Unix-Zeilenenden speichert. Ich habe meine Datei mit Zeilenumbrüchen in Hex konvertiert und sie verwendet definitiv nur "0a" als Zeilenumbruchzeichen.

Ich habe viel gegoogelt und obwohl viele Seiten den Zeilenumbruch gefolgt von der Rücktaste dokumentieren, erklärt keine, die ich gefunden habe, warum die Rücktaste erforderlich ist.

-David.

user431931
quelle
Könnte jemand bitte sagen, wo er solchen Code sieht? Vielen Dank.
Spbnick
@spbnick Sie müssen das IFS zurücksetzen, wenn Sie über Dateien iterieren möchten, z for file in $(ls) ; do echo "$f" ; done. B. in . Ohne das IFS nur auf Zeilenumbrüche zu setzen, gibt die for-Schleife jedes durch Leerzeichen getrennte Stück in jedem Dateinamen einzeln anstelle des gesamten Dateinamens wieder.
bleistift2
Danke @ bleistift2, ich verstehe, warum jemand IFS ändern möchte und ich verstehe, wie der Code funktioniert (meine Antwort ist hier die beliebteste). Ich bin nur neugierig, woher diese spezielle Verwendung von Backspace-Zeichen in IFS stammt und wer sie historisch erfunden hat. Daher frage ich, wo die Leute diese Art von Code gesehen haben.
Spbnick

Antworten:

128

Denn wie im Bash-Handbuch zur Befehlsersetzung steht :

Bash führt die Erweiterung durch, indem der Befehl ausgeführt und die Befehlsersetzung durch die Standardausgabe des Befehls ersetzt wird, wobei alle nachfolgenden Zeilenumbrüche gelöscht werden.

Durch Hinzufügen \bverhindern Sie also das Entfernen von \n.

Ein sauberer Weg, dies zu tun, könnte darin bestehen, $''Zitate wie folgt zu verwenden :

IFS=$'\n'
spbnick
quelle
1
$'...'ist für die Aufnahme in POSIX vorgesehen. [ mywiki.wooledge.org/Bashism] [ austingroupbugs.net/view.php?id=249]
go2null
1
Obwohl diese irgendwann in Posix enthalten sein sollen, sind sie dies derzeit nicht und dies funktioniert an vielen Stellen nicht mit / bin / sh, die mit einer Posix-Shell verknüpft sind, nicht mit Bash.
John Eikenberry
1
Aber wenn ich das Array mit newline und read Befehl teilen möchte. Nur das erste Element wird dem hostArray hinzugefügt. Aber der Gastgeber hat 4 Gegenstände. host=$(mysql -S $socket $catalog --skip-column-names -e "select host from mysql.user); echo "Host: $host"; IFS=$'\n';read -ra hostArray <<< "$host"; echo "HostArray:$hostArray" #This prints only 1 item. But there are 4 items;Was ist hier falsch?
Guna
55

Ich erinnerte mich nur an den einfachsten Weg. Getestet mit Bash auf Debian Keuchen.

IFS="
"

Im Ernst :)

moddie
quelle
3
aber es ist kein Einzeiler 😛 - besonders wenn Sie das alte $ IFS wiederherstellen müssen, wie in: stackoverflow.com/questions/1406966/…
juanmirocks
1
Mann, du hast meinen Tag gemacht! Kein Scherz und funktioniert auch in posix.
Aleksey Balenko
12

Es ist ein Hack wegen der Verwendung echound Befehlsersetzung.

prompt> x=$(echo -en "\n")
prompt> echo ${#x}
0
prompt> x=$(echo -en "\n\b")
prompt> echo ${#x}
2

Die $()Streifen folgen nachgestellten Zeilenumbrüchen und \bverhindern, \ndass es sich um nachgestellte Zeilenumbrüche handelt, während es höchst unwahrscheinlich ist, dass sie in einem Text erscheinen. IFS=$'\n'ist der bessere Weg, um IFS so einzustellen, dass es in Zeilenumbrüchen aufgeteilt wird.

Alter Pro
quelle
2
Dies zeigt, dass die \n\bFormation auch die Rücktaste als IFSTrennzeichen enthält, sodass ich dies unter bestimmten Umständen als fehlerhaft betrachten würde.
Tom Hale
3

\bchar als Suffix von newline \nwird hinzugefügt, da das Ende \nder Befehlssubstitution entfernt wurde, $(...)sodass das \bals Suffix für verwendet wird \nund dadurch \nnicht mehr nachgestellt wird, sodass es von der Befehlssubstitution zurückgegeben wird.

Der Nebeneffekt, der darin besteht, dass IFSneben diesem auch ein \bZeichen und ein Trennzeichen vorhanden sind \n, ist das alleinige Interesse.

Wenn Sie erwarten, dass \beines Tages in der Zeichenfolge etwas passiert (warum nicht?), Können Sie Folgendes verwenden:

IFS="$(printf '\nx')" && IFS="${IFS%x}";

Das returns \n suffixed with x && removes x

Jetzt hat IFS nur noch das \nZeichen.

IFS="$(printf '\nx')" && IFS="${IFS%x}";
echo ${#IFS}; # 1

und nichts wird im Falle von \bTest brechen :

#!/bin/sh

sentence=$(printf "Foo\nBar\tBaz Maz\bTaz");
IFS="$(printf '\nx')" && IFS="${IFS%x}";

for entry in $sentence
do
    printf "Entry: ${entry}.\n";
done

gibt zwei Zeilen (wegen einer \n):

Entry: Foo.
Entry: Bar      Baz Maz Taz.

wie erwartet.

anstatt zu IFS="$(printf '\nx')" && IFS="${IFS%x}";verwenden:

IFS="
"

gibt das gleiche Ergebnis, aber diese beiden Zeilen dürfen nicht eingerückt werden. Wenn Sie versehentlich Leerzeichen oder Tabulatoren oder andere weiße Zeichen zwischen "und" setzen, haben Sie dort nicht mehr nur \nZeichen, sondern auch einige "Boni". Das ist schwer zu tun Stelle, es sei denn, Sie verwenden die Option Alle Zeichen in Ihrem Editor anzeigen.

Jimmix
quelle
1

Das Drücken der Eingabetaste ohne Zuweisung funktioniert ebenfalls. Obwohl es so aussieht, als hätte jemand einen Fehler gemacht und ist schwer zu verstehen.

IFS=
#This is a line
Taiyebur
quelle
1
Setzt dies $ IFS nicht auf eine leere Zeichenfolge?
Marius Gedminas
Nun, Sie drücken die Eingabetaste nach dem '=' - Zeichen, damit es funktioniert. Es hat funktioniert, als ich es versuchte. @ moddies Antwort sieht klarer aus.
Taiyebur