Neue Zeile in Bash-Variablen

9

Ich versuche, mehrere Zeilen in einer Bash-Variablen zu speichern, aber es scheint nicht zu funktionieren.

Wenn ich zum Beispiel /bineine Datei pro Zeile aufführe und darin speichere $LS, übergebe ich $LSals stdin an wcimmer 1:

$ ls -1 /bin | wc -l
134
$ LS=$(ls -1 /bin); wc -l <<< $LS
1

Wenn ich versuche, auf dem Bildschirm auszugeben, erhalte ich verschiedene Ergebnisse: echoDruckt alle Zeilen in einer einzelnen Zeile, während printfnur die erste Zeile gedruckt wird:

#!/bin/bash
LS=$(ls -1 /bin)
echo $LS
printf $LS

Eine Bash-Variable kann also mehrere Zeilen enthalten?

Wizard79
quelle

Antworten:

15

Sie müssen es in doppelte Anführungszeichen setzen (und in den meisten Fällen sollten Sie Variablen in doppelte Anführungszeichen setzen ):

echo "$LS"

Aber nicht verwendet echo Variablen Inhalt zu drucken, mit printf statt :

printf '%s\n' "$LS"
cuonglm
quelle
1
Um die Gesamtantwort genauer zu formulieren: printf erwartet, dass eine Formatzeichenfolge der erste Parameter ist, weshalb sie nicht gesehen haben, was sie erwartet haben. Wenn sie Zeilenumbrüche in der Ausgabe von printf sehen möchten, die willkürlich auf Leerzeichen aufgeteilt sind, printf "%s\n" $LSwürde dies der Fall sein .
Jeff Schaller
Die% LS sollte übrigens $ LS sein (ich konnte keine so kurze Bearbeitung vornehmen)
Jeff Schaller
Vielen Dank! Es funktioniert natürlich auch mit wcund anderen Befehlen:wc -l <<< "$LS"
Wizard79
1
@JeffSchaller: Nein, in diesem Fall sollten Sie die Variable nicht aus dem Zitat entfernen, sondern nur verwenden, printf '%s\n' "$LS"wenn Sie Newline sehen möchten. Das ist eine falsche Eingabe, behoben!
Cuonglm
Warum sollten Sie Echo nicht zum Drucken von Variablen verwenden?
123
9

Die Zeilenumbrüche befinden sich in der Variablen. LS=$(ls -1)Setzt die Variable LSauf die Ausgabe von ls -1(die lsübrigens dieselbe Ausgabe erzeugt , außer wenn die Ausgabe an ein Terminal geht) abzüglich nachfolgender Zeilenumbrüche.

Das Problem ist, dass Sie die Zeilenumbrüche entfernen, wenn Sie den Wert ausdrucken. In einem Shell-Skript $LSbedeutet dies nicht "den Wert der Variablen LS", sondern "den Wert von nehmen LS, ihn in Wörter aufteilen IFSund jedes Wort als Glob-Muster interpretieren". Um den Wert von zu erhalten LS, müssen Sie schreiben "$LS"oder allgemeiner $LSzwischen doppelte Anführungszeichen setzen.

echo "$LS"Gibt den Wert von aus LS, außer in einigen Shells, die Backslash-Zeichen interpretieren, und mit Ausnahme einiger Werte, die mit beginnen -.

printf "$LS"Gibt den Wert von LSaus, solange er kein Prozent- oder Backslash-Zeichen enthält und (bei den meisten Implementierungen) nicht mit beginnt -.

LSVerwenden Sie, um den Wert von genau zu drucken printf %s "$LS". Wenn Sie am Ende einen Zeilenumbruch wünschen, verwenden Sie printf '%s\n' "$LS".

Beachten Sie, dass $(ls)dies im Allgemeinen nicht die Liste der Dateien im aktuellen Verzeichnis ist. Dies funktioniert nur, wenn Sie ausreichend zahme Dateinamen haben. Um die Liste der Dateinamen (außer Punktdateien) abzurufen, müssen Sie einen Platzhalter verwenden : *. Das Ergebnis ist eine Liste von Zeichenfolgen, keine Zeichenfolge. Sie können sie daher keiner Zeichenfolgenvariablen zuweisen. Sie können eine Array-Variable files=(*)in Shells verwenden, die sie unterstützen (ksh93, bash, zsh).

Weitere Informationen finden Sie unter Warum verschluckt sich mein Shell-Skript an Leerzeichen oder anderen Sonderzeichen?

Gilles 'SO - hör auf böse zu sein'
quelle
printf "$ LS" würde auch dem Parsen von Backslash-Zeichen erliegen, ähnlich wie Echo
cnst
0

Das vorgenannte

printf '%s\n' "$LS"

ist die einzig richtige Lösung.

Lassen Sie mich zeigen, dass die anderen vorgeschlagenen Lösungen mit echound printfeinfach nicht richtig funktionieren:

$ mkdir t
$ cd t
$ touch \\n
$ LS=$(ls -l)
$ echo "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf "$LS\n"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf '%s\n' "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ ls -l
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ rm \\n
$ cd ..
$ rmdir t
$

(Man kann auch mit V=$(printf 'line0:\\n\\n\nline1.\n') printf '%s\n' "$V"und Variationen testen .)

Während \ndie Dateinamen möglicherweise nicht so häufig vorkommen, speichern Sie sie git diffin einer Variablen, und Ihre Chancen, auf Literal \nzu stoßen, steigen dramatisch.

cnst
quelle
Beachten Sie, dass kshund zshauch unterstützen print -r -- "$LS"und zshauch hat echo -E - $LS. cshhat echo $LS:qjedoch das Zeilenumbruchzeichen nicht ausgegeben, wenn $ LS leer ist, und es ist in csh ziemlich schwierig, einen mehrzeiligen Wert in $ LS zu bekommen.
Stéphane Chazelas