Was ist die genaue Bedeutung von IFS = $ '\ n'?

123

Wenn das folgende Beispiel, das die IFSUmgebungsvariable auf ein Zeilenvorschubzeichen setzt ...

IFS=$'\n'
  • Was bedeutet die Dollarzeichen bedeuten genau ?
  • Was macht es in diesem speziellen Fall?
  • Wo kann ich mehr über diese spezielle Verwendung lesen (Google lässt keine Sonderzeichen in Suchanfragen zu und ich weiß nicht, wonach ich sonst suchen soll)?

Ich weiß, was die IFSUmgebungsvariable ist und was das \nZeichen ist (Zeilenvorschub), aber warum nicht einfach das folgende Formular verwenden: IFS="\n" (was nicht funktioniert)?

Wenn ich beispielsweise jede Zeile einer Datei durchlaufen und eine for-Schleife verwenden möchte, kann ich Folgendes tun:

for line in (< /path/to/file); do
    echo "Line: $line"
done

Dies funktioniert jedoch nur dann richtig, wenn IFSein Zeilenvorschubzeichen festgelegt ist. Damit es funktioniert, müsste ich Folgendes tun:

OLDIFS=$IFS
IFS=$'\n'
for line in (< /path/to/file); do
    echo "Line: $line"
done
IFS=$OLDIFS

Hinweis: Ich brauche keinen anderen Weg, um dasselbe zu tun, ich kenne bereits viele andere ... Ich bin nur neugierig $'\n'und frage mich, ob mir jemand eine Erklärung dazu geben könnte.

Yanick Girouard
quelle

Antworten:

161

Normalerweise werden bashEscape-Sequenzen in String-Literalen nicht interpretiert. Wenn Sie also schreiben \noder "\n"oder '\n', ist dies kein Zeilenumbruch - es ist der Buchstabe n(im ersten Fall) oder ein Backslash, gefolgt vom Buchstaben n(in den beiden anderen Fällen).

$'somestring'ist eine Syntax für String-Literale mit Escape-Sequenzen . Also im Gegensatz zu '\n', $'\n'ist eigentlich ein Zeilenumbruch.

sepp2k
quelle
2
Nicht genau so - \nist nur ein (entkommener) Buchstabe n. Sie haben Recht damit '\n'und "\n"sind Spiel, gefolgt von n.
Roman Cheplyaka
15
Beachten Sie, dass dies $'\n'bash-spezifisch ist - es funktioniert nicht in einer POSIX-Shell ( /bin/sh). Um den gleichen Effekt auf POSIX-konforme Weise zu erzielen, können Sie IFS='eingeben, dann die Eingabetaste drücken, um ein tatsächliches Zeilenumbruchzeichen einzugeben, und dann den Abschluss eingeben'
Richard Hansen
23
IFS=$(echo -e '\n')sollte es auch auf POSIX-kompatible Weise tun.
Vineet
12
@Vineet - es gab mir eine Pause, um einen positiv bewerteten Kommentar zu bestreiten. Dies ist zwar Posix-korrekt, funktioniert aber nicht - Die Befehlsersetzungsoperatoren in bash entfernen alle nachgestellten Zeilenumbrüche. Sehen Sie dies für weitere Einzelheiten .
Digitales Trauma
9
@DigitalTrauma Ich denke, es ist nicht einmal POSIX: -eist nicht definiert und funktioniert \nohne -eXSI-Erweiterung: pubs.opengroup.org/onlinepubs/9699919799/utilities/… . printf '\n'Felsen;)
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
20

Nur um dem Konstrukt seinen offiziellen Namen zu geben : Zeichenfolgen der Form $'...'werden als ANSI C-Zeichenfolgen bezeichnet .

Das heißt, wie in [ANSI] C-Zeichenfolgen werden Spiel-Escape-Sequenzen erkannt und auf ihr Literaläquivalent erweitert (siehe unten für die vollständige Liste der unterstützten Escape-Sequenzen).

Nach dieser Erweiterung $'...'verhalten sich Zeichenfolgen genauso wie '...'Zeichenfolgen, dh sie werden als Literale behandelt, die KEINEN [weiteren] Shell-Erweiterungen unterliegen .

$'\n'Erweitert sich beispielsweise zu einem wörtlichen Zeilenumbruchzeichen - was ein reguläres Bash-String-Literal (ob '...'oder nicht "...") nicht kann. [1]

Ein weiteres interessantes Merkmal ist, dass ANSI-Zeichenfolgen in C-Anführungszeichen '(einfache Anführungszeichen) entweichen können, wie\' dies bei '...'(regulären Zeichenfolgen in einfachen Anführungszeichen) nicht möglich ist:

echo $'Honey, I\'m home' # OK; this cannot be done with '...'

Liste der unterstützten Escape-Sequenzen :

Backslash-Escape-Sequenzen werden, falls vorhanden, wie folgt dekodiert:

\ eine Warnung (Glocke)

\ b Rücktaste

\ e \ E ein Escapezeichen (nicht ANSI C)

\ f Formularvorschub

\ n Newline

Wagenrücklauf

\ t horizontale Registerkarte

\ v vertikale Registerkarte

\ backslash

Einfaches Anführungszeichen

"doppeltes Anführungszeichen

\ nnn das Acht-Bit-Zeichen, dessen Wert der Oktalwert nnn ist (ein bis drei Ziffern)

\ xHH das Acht-Bit-Zeichen, dessen Wert der Hexadezimalwert HH ist (eine oder zwei Hexadezimalstellen)

\ uHHHH das Unicode-Zeichen (ISO / IEC 10646), dessen Wert der Hexadezimalwert HHHH ist (ein bis vier Hexadezimalstellen)

\ UHHHHHHHH das Unicode-Zeichen (ISO / IEC 10646), dessen Wert der Hexadezimalwert HHHHHHHH ist (ein bis acht Hexadezimalstellen)

\ cx ein Steuerzeichen x

Das erweiterte Ergebnis wird in einfachen Anführungszeichen gesetzt, als ob das Dollarzeichen nicht vorhanden gewesen wäre.


[1] Sie können jedoch tatsächliche Zeilenumbrüche in Zeichenfolgen "..." und "..." einbetten . Sie können also Zeichenfolgen definieren, die mehrere Zeilen umfassen.

mklement0
quelle
16

Von http://www.linuxtopia.org/online_books/bash_guide_for_beginners/sect_03_03.html :

Wörter in der Form "$ 'STRING'" werden auf besondere Weise behandelt. Das Wort wird zu einer Zeichenfolge erweitert, wobei Zeichen mit Backslash-Escapezeichen gemäß ANSI-C-Standard ersetzt werden. Backslash-Escape-Sequenzen finden Sie in der Bash-Dokumentation

Ich denke, es zwingt das Skript, dem Zeilenvorschub zum richtigen ANSI-C-Standard zu entkommen.

Brad Swerdfeger
quelle
8

Die Wiederherstellung des Standard-IFS OLDIFS=$IFSist nicht erforderlich. Führen Sie ein neues IFS in der Subshell aus, um zu vermeiden, dass das Standard-IFS überschrieben wird:

ar=(123 321); ( IFS=$'\n'; echo ${ar[*]} )

Außerdem glaube ich nicht wirklich, dass Sie das alte IFS vollständig wiederherstellen. Sie sollten es doppelt zitieren, um Zeilenumbrüche wie z OLDIFS="$IFS".

Marek
quelle
2
Dies ist eine wirklich nützliche Technik. Ich habe es gerade für eine sauberere Shell verwendet args=$(IFS='&'; echo "$*"). Auf eine Bourne-Shell-freundliche Art und Weise wiederherzustellen IFS, $' \t\n'ist keine leichte Aufgabe.
Jeberle
Re Besides I don't really believe you recover the old IFS fully: Wort Spaltung wird nicht auf der rechte Seite von Variablenzuweisungen durchgeführt (aber Zitat Entfernung ist), so OLDIFS=$IFSund OLDIFS="$IFS"die gleiche Art und Weise verhalten.
mklement0
3

ANSI C-Zeichenfolgen sind ein wichtiger Punkt. Danke an @ mklement0.

Sie können ANSI C-Zeichenfolgen mit dem Befehl od testen.

echo -n $'\n' | od -c
echo -n '\n' | od -c
echo -n $"\n" | od -c
echo -n "\n" | od -c

Ausgänge:

0000000  \n  
0000001

0000000   \   n   
0000002

0000000   \   n   
0000002

0000000   \   n   
0000002

Sie können die Bedeutung anhand der Ausgänge klar erkennen.

Großer Schild
quelle
-7

Es ist wie das Abrufen des Werts aus einer Variablen:

VAR='test'
echo VAR
echo $VAR

sind unterschiedlich, so dass das Dollarzeichen im Grunde den Inhalt bewertet.

Pieter
quelle
6
Dies hat nichts mit Variablen zu tun. $'FOO'(im Gegensatz zu $FOOdem, worum es bei dieser Frage nicht ging) ist ein String-Literal. Wenn Sie ausführen echo $'VAR', werden Sie sehen, dass die Zeichenfolge VARnicht gedruckt wird test.
sepp2k