Hier sind eine Reihe von Fällen, in denen echo $var
ein anderer Wert als der gerade zugewiesene angezeigt werden kann. Dies geschieht unabhängig davon, ob der zugewiesene Wert "doppelt in Anführungszeichen", "einfach in Anführungszeichen" oder nicht in Anführungszeichen gesetzt wurde.
Wie bringe ich die Shell dazu, meine Variable richtig einzustellen?
Sternchen
Die erwartete Ausgabe ist /* Foobar is free software */
, aber stattdessen erhalte ich eine Liste von Dateinamen:
$ var="/* Foobar is free software */"
$ echo $var
/bin /boot /dev /etc /home /initrd.img /lib /lib64 /media /mnt /opt /proc ...
Eckige Klammern
Der erwartete Wert ist [a-z]
, aber manchmal bekomme ich stattdessen einen einzelnen Buchstaben!
$ var=[a-z]
$ echo $var
c
Zeilenvorschübe (Zeilenumbrüche)
Der erwartete Wert ist eine Liste separater Zeilen, aber stattdessen befinden sich alle Werte in einer Zeile!
$ cat file
foo
bar
baz
$ var=$(cat file)
$ echo $var
foo bar baz
Mehrere Leerzeichen
Ich habe einen sorgfältig ausgerichteten Tabellenkopf erwartet, aber stattdessen verschwinden mehrere Leerzeichen oder werden zu einem zusammengefasst!
$ var=" title | count"
$ echo $var
title | count
Tabs
Ich habe zwei durch Tabulatoren getrennte Werte erwartet, aber stattdessen erhalte ich zwei durch Leerzeichen getrennte Werte!
$ var=$'key\tvalue'
$ echo $var
key value
var=$(cat file)
also in Ordnung, wird aberecho "$var"
benötigt.Antworten:
In allen oben genannten Fällen ist die Variable korrekt eingestellt, aber nicht richtig gelesen! Der richtige Weg ist, doppelte doppelte Anführungszeichen zu verwenden, wenn auf Folgendes verwiesen wird :
Dies ergibt den erwarteten Wert in allen angegebenen Beispielen. Zitieren Sie immer variable Referenzen!
Warum?
Wenn eine Variable nicht in Anführungszeichen gesetzt ist , wird Folgendes ausgeführt :
Führen Sie eine Feldaufteilung durch , bei der der Wert im Leerzeichen (standardmäßig) in mehrere Wörter aufgeteilt wird:
Vor:
/* Foobar is free software */
Nachher:
/*
,Foobar
,is
,free
,software
,*/
Jedes dieser Wörter wird einer Pfadnamenerweiterung unterzogen , bei der Muster in übereinstimmende Dateien erweitert werden:
Vor:
/*
Nachher:
/bin
,/boot
,/dev
,/etc
,/home
, ...Schließlich werden alle Argumente an echo übergeben, das sie durch einzelne Leerzeichen getrennt ausgibt und gibt
anstelle des Wertes der Variablen.
Wenn die Variable in Anführungszeichen gesetzt wird , wird:
Aus diesem Grund sollten Sie immer alle Variablenreferenzen zitieren , es sei denn, Sie benötigen speziell eine Wortaufteilung und eine Pfadnamenerweiterung. Tools wie Shellcheck helfen Ihnen und warnen in allen oben genannten Fällen vor fehlenden Anführungszeichen.
quelle
$(..)
Streifen hinter den Zeilenvorschüben. Sie können es umgehenvar=$(cat file; printf x); var="${var%x}"
.Vielleicht möchten Sie wissen, warum dies geschieht. Finden Sie zusammen mit der großartigen Erklärung dieses anderen Mannes eine Referenz zu Warum erstickt mein Shell-Skript an Leerzeichen oder anderen Sonderzeichen? geschrieben von Gilles in Unix & Linux :
quelle
Benutzer doppeltes Anführungszeichen, um den genauen Wert zu erhalten. so was:
und es wird Ihren Wert korrekt lesen.
quelle
Neben anderen Problemen , verursacht durch Fehler, zu zitieren
-n
und-e
kann verbraucht werdenecho
als Argumente. (Nur Ersteres ist gemäß der POSIX-Spezifikation für legalecho
, aber mehrere gängige Implementierungen verstoßen gegen die Spezifikation und verbrauchen-e
auch).Um dies zu vermeiden, verwenden Sie
printf
anstelle von,echo
wenn Details wichtig sind.So:
Durch korrektes Zitieren werden Sie jedoch nicht immer gerettet, wenn Sie Folgendes verwenden
echo
:... während es wird Sie mit speichern
printf
:quelle
-e
/-n
/ Backslash nicht angezeigt?" Wir können von hier aus gegebenenfalls Links hinzufügen.-n
auch ?-e
. Der Standard fürecho
gibt die Ausgabe nicht an, wenn das erste Argument lautet-n
, sodass in diesem Fall alle möglichen Ausgaben zulässig sind. es gibt keine solche Bestimmung für-e
.echo $var
Die Ausgabe hängt stark vom Wert derIFS
Variablen ab. Standardmäßig enthält es Leerzeichen, Tabulatoren und Zeilenumbrüche:Dies bedeutet, dass die Shell bei der Feldaufteilung (oder Wortaufteilung) alle diese Zeichen als Worttrennzeichen verwendet. Dies geschieht, wenn auf eine Variable ohne doppelte Anführungszeichen verwiesen wird, um sie wiederzugeben (
$var
), und daher die erwartete Ausgabe geändert wird.Eine Möglichkeit, die Wortteilung zu verhindern (neben der Verwendung von doppelten Anführungszeichen), besteht darin,
IFS
den Wert auf Null zu setzen . Siehe http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 :Das Setzen auf Null bedeutet das Setzen auf einen leeren Wert:
Prüfung:
quelle
set -f
verhindern, dass GlobbingIFS
auf null gesetzt,echo $var
wird erweitertecho '/* Foobar is free software */'
und Expansionspfad ist nicht in einzelnen Strings in Anführungszeichen ausgeführt.mkdir "/this thing called Foobar is free software etc/"
sehen, dass es sich immer noch ausdehnt. Für das[a-z]
Beispiel ist es offensichtlich praktischer .[a-z]
Beispiel Sinn .Die Antwort von ks1322 hat mir geholfen, das Problem bei der Verwendung zu identifizieren
docker-compose exec
:Wenn Sie das
-T
Flag weglassen ,docker-compose exec
fügen Sie ein Sonderzeichen hinzu, das die Ausgabe unterbricht. Wir sehenb
stattdessen1b
:Funktioniert mit
-T
flagdocker-compose exec
wie erwartet:quelle
Zusätzlich zum Anführungszeichen der Variablen könnte man auch die Ausgabe der Variablen übersetzen
tr
und Leerzeichen in Zeilenumbrüche konvertieren.Dies ist zwar etwas komplizierter, bietet jedoch mehr Vielfalt bei der Ausgabe, da Sie jedes Zeichen als Trennzeichen zwischen Arrayvariablen einsetzen können.
quelle
tr
umgekehrt, um Arrays aus Textdateien zu erstellen.tr
erforderlich, ein Array ordnungsgemäß / korrekt aus einer Textdatei zu erstellen. Sie können das gewünschte Trennzeichen angeben, indem Sie IFS festlegen. Zum Beispiel: Funktioniert denIFS=$'\n' read -r -d '' -a arrayname < <(cat file.txt && printf '\0')
ganzen Weg zurück durch Bash 3.2 (die älteste Version in großer Verbreitung) und setzt den Exit-Status korrekt auf false, wenn Ihr Fehlercat
aufgetreten ist. Und wenn man wollte, sagen wir, Tabs statt Zeilenumbrüche, würden Sie einfach ersetzen die$'\n'
mit$'\t'
.arrayname=( $( cat file | tr '\n' ' ' ) )
, dann ist das auf mehreren Ebenen kaputt: Es überträgt Ihre Ergebnisse (so*
wird eine Liste von Dateien im aktuellen Verzeichnis), und es würde genauso gut funktionieren ohnetr
( oder dascat
, was man betrifft, man könnte es einfach benutzenarrayname=$( $(<file) )
und es würde auf die gleiche Weise kaputt gehen, aber weniger ineffizient).