Wie kann ich testen, ob eine Variable leer ist oder nur Leerzeichen enthält?

240

Die folgende Bash-Syntax überprüft, ob paramnicht leer ist:

 [[ !  -z  $param  ]]

Zum Beispiel:

param=""
[[ !  -z  $param  ]] && echo "I am not zero"

Keine Ausgabe und es ist in Ordnung.

Wenn paramaber bis auf ein (oder mehrere) Leerzeichen leer ist, ist der Fall anders:

param=" " # one space
[[ !  -z  $param  ]] && echo "I am not zero"

"Ich bin nicht Null" wird ausgegeben.

Wie kann ich den Test ändern, um Variablen, die nur Leerzeichen enthalten, als leer zu betrachten?

maihabunash
quelle
4
Von man test: -z STRING - the length of STRING is zero. Wenn Sie alle Leerzeichen entfernen möchten $param, verwenden Sie${param// /}
dchirikov
6
WOW, es gibt überhaupt keine trim()in * nix integrierte einfache Funktion? So viele verschiedene Hacks, um etwas so Einfaches zu erreichen ...
ADTC
6
@ADTC $ (sed 's / ^ \ s + | \ s + $ // g' <<< $ string) scheint mir einfach genug zu sein. Warum das Rad neu erfinden?
Bogenschütze
3
Weil es glänzender sein könnte
Inversus
1
Ich stelle nur fest, dass dies [[ ! -z $param ]]gleichbedeutend ist mit test ! -z $param.
Noah Sussman

Antworten:

292

Beachten Sie zunächst, dass der -zTest explizit für Folgendes bestimmt ist:

Die Länge der Zeichenfolge ist Null

Das heißt, eine Zeichenfolge, die nur Leerzeichen enthält, sollte unter nicht wahr sein -z, da sie eine Länge ungleich Null hat.

Was Sie wollen, ist, die Leerzeichen aus der Variablen mit der Musterersetzungsparameter-Erweiterung zu entfernen :

[[ -z "${param// }" ]]

Dies erweitert die paramVariable und ersetzt alle Übereinstimmungen des Musters (ein einzelnes Leerzeichen) durch nichts, sodass eine Zeichenfolge, die nur Leerzeichen enthält, zu einer leeren Zeichenfolge erweitert wird.


Das Wesentliche an der Funktionsweise ist, dass ${var/pattern/string}das erste Spiel patternmit dem längsten ersetzt wird string. Wenn patternmit /(wie oben) begonnen wird, werden alle Übereinstimmungen ersetzt. Da die Ersetzung leer ist, können wir das Finale /und den stringWert weglassen :

$ {Parameter / Muster / Zeichenfolge}

Das Muster wird wie bei der Dateinamenerweiterung zu einem Muster erweitert. Der Parameter wird erweitert und die längste Übereinstimmung des Musters mit seinem Wert wird durch einen String ersetzt . Wenn das Muster mit '/' beginnt, werden alle Übereinstimmungen des Musters durch eine Zeichenfolge ersetzt . Normalerweise wird nur die erste Übereinstimmung ersetzt. ... Wenn der String null ist, werden Übereinstimmungen mit dem Muster gelöscht und das / folgende Muster kann weggelassen werden.

Danach müssen wir ${param// }alle Leerzeichen löschen.

Beachten kshSie , dass die Syntax , obwohl sie in (woher sie stammt) zshund vorhanden bashist, nicht POSIX ist und nicht in shSkripten verwendet werden sollte.

Michael Homer
quelle
Verwenden sedSie sie sagten ... nur echodie Variable, die sie sagten ...
Mikezter
1
Hmm. Wenn es so ist, ${var/pattern/string}sollte es dann nicht [[ -z ${param/ /} ]]mit dem Abstand zwischen den Schrägstrichen sein, um das Muster und nichts danach zu sein, um die Zeichenfolge zu sein?
Jesse Chisholm
@JesseChisholm "Da die Ersetzung leer ist, können wir den finalen / und den String-Wert weglassen"; Msgstr "Wenn der String Null ist, werden Übereinstimmungen mit dem Muster gelöscht und das / folgende Muster kann weggelassen werden."
Michael Homer
6
@MichaelHomer Die Antwort [[ -z "${param// }" ]]sieht sicher so aus, als ob das patternleer und das replacement stringein einzelnes Leerzeichen ist. AH! Ich sehe es jetzt, da if pattern begins with a slashich diesen Teil verpasst habe. Das Muster ist also / und die Zeichenfolge ist weggelassen und daher leer. Vielen Dank.
Jesse Chisholm
bash note: Wenn in set -o nounset (set -u) ausgeführt wird und param nicht gesetzt ist (statt null oder mit Leerzeichen gefüllt), wird ein ungebundener Variablenfehler generiert. (set + u; .....) würde diesen Test ausnehmen.
Brian Chrisman
31

Die einfache Möglichkeit, zu überprüfen, ob eine Zeichenfolge nur Zeichen in einem autorisierten Satz enthält, besteht darin, zu prüfen, ob nicht autorisierte Zeichen vorhanden sind. Anstatt also zu testen, ob die Zeichenfolge nur Leerzeichen enthält, sollten Sie testen, ob die Zeichenfolge andere Zeichen als Leerzeichen enthält. In bash, ksh oder zsh:

if [[ $param = *[!\ ]* ]]; then
  echo "\$param contains characters other than space"
else
  echo "\$param consists of spaces only"
fi

"Besteht nur aus Leerzeichen" schließt den Fall einer leeren (oder nicht gesetzten) Variablen ein.

Möglicherweise möchten Sie nach Whitespace-Zeichen suchen. Verwenden [[ $param = *[^[:space:]]* ]]Sie diese [[ $param = *[$' \t\n']* ]]Option , um Gebietsschemaeinstellungen oder eine explizite Liste von Leerzeichen zu verwenden, auf die Sie testen möchten, z. B. um Leerzeichen, Tabulatoren oder Zeilenumbrüche zu testen.

Das Abgleichen einer Zeichenfolge mit einem Muster mit =inside [[ … ]]ist eine ksh-Erweiterung (auch in bash und zsh vorhanden). In jedem Bourne / POSIX-Stil können Sie das caseKonstrukt verwenden, um eine Zeichenfolge mit einem Muster abzugleichen. Beachten Sie, dass Standard-Shell-Muster verwendet werden !, um einen Zeichensatz zu negieren, anstatt ^wie in den meisten regulären Ausdruckssyntaxen.

case "$param" in
  *[!\ ]*) echo "\$param contains characters other than space";;
  *) echo "\$param consists of spaces only";;
esac

Zum Testen auf Leerzeichen ist die $'…'Syntax spezifisch für ksh / bash / zsh. Sie können diese Zeichen buchstäblich in Ihr Skript einfügen (beachten Sie, dass eine neue Zeile in Anführungszeichen stehen muss, da Backslash + Newline zu nichts erweitert werden) oder sie generieren, z

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac
Gilles
quelle
Das ist fast exzellent - aber es beantwortet die gestellte Frage noch nicht. Derzeit können Sie den Unterschied zwischen einer nicht gesetzten oder leeren Shell-Variablen und einer Variablen erkennen, die nur Leerzeichen enthält. Wenn Sie meinen Vorschlag akzeptieren, fügen Sie das param-Erweiterungsformular hinzu, das noch nicht von einer anderen Antwort verarbeitet wurde, die explizit eine Shell-Variable auf das Setzen testet. Ich meine ${var?not set}, wenn Sie diesen Test bestehen und überleben, würde dies sicherstellen, dass eine Variable, die mit Ihrer übereinstimmt, *) caseeine endgültige Wirkung hat antworte zum Beispiel.
mikeserv
Korrektur - dies würde in der Tat die ursprünglich gestellte Frage beantworten, wurde jedoch inzwischen so bearbeitet, dass sich die Bedeutung der Frage grundlegend ändert und Ihre Antwort ungültig wird.
MikeServ
Sie brauchen [[ $param = *[!\ ]* ]]in mksh.
Stéphane Chazelas
20

POSIXly:

case $var in
  (*[![:blank:]]*) echo '$var contains non blank';;
  (*) echo '$var contains only blanks or is empty or unset'
esac

So unterscheiden Sie zwischen leer , nicht leer , leer und nicht gesetzt :

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank
esac

[:blank:]ist für Zeichen mit horizontalem Abstand (Leerzeichen und Tabulator in ASCII, aber wahrscheinlich sind noch einige in Ihrem Gebietsschema vorhanden; einige Systeme enthalten das nicht unterbrechende Leerzeichen (sofern verfügbar), andere nicht). Wenn Sie auch Zeichen mit vertikalem Abstand (z. B. Zeilenvorschub oder Formularvorschub) verwenden möchten, ersetzen Sie diese [:blank:]durch [:space:].

Stéphane Chazelas
quelle
POSIXly ist ideal, weil es mit dem (wohl besseren) Gedankenstrich funktioniert.
Ken Sharp
zshScheint ein Problem damit zu haben [![:blank:]], es :bist ein ungültiger Modifikator. In shund kshemulieren, es Ereignis nicht gefunden für[
cuonglm
@ Cuonglm, was hast du versucht? var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'ist für mich ok. Das nicht gefundene Ereignis ist nur für interaktive Shells gedacht.
Stéphane Chazelas
@ StéphaneChazelas: Ah, richtig, ich habe es mit interaktiven Shells versucht. Der :bungültige Modifikator ist etwas seltsam.
Cuonglm
1
@FranklinYu auf O / X shist bashaufgebaut mit --enable-xpg-echo-defaultund --enable-strict-posix-defaultum Unix - konform (welches beinhaltet sein zum echo '\t'Ausgeben eines TAB).
Stéphane Chazelas
4

Der einzige verbleibende Grund, ein Shell-Skript anstelle eines Skripts in einer guten Skriptsprache zu schreiben , ist, wenn extreme Portabilität ein vorrangiges Anliegen ist. Das Erbe /bin/shist die einzige Sache , die Sie sicher sein können , die Sie haben, aber Perl zum Beispiel ist eher wahrscheinlich verfügbar Cross-Plattform als Bash sein. Schreiben Sie daher niemals Shell-Skripte, die Funktionen verwenden, die nicht wirklich universell sind - und denken Sie daran, dass mehrere proprietäre Unix-Anbieter ihre Shell-Umgebung vor POSIX.1-2001 eingefroren haben .

Es gibt eine tragbare Möglichkeit, diesen Test durchzuführen, aber Sie müssen Folgendes verwenden tr:

[ "x`printf '%s' "$var" | tr -d "$IFS"`" = x ]

(Der Standardwert von $IFSist günstigerweise ein Leerzeichen, ein Tabulator und eine neue Zeile.)

(Das printfeingebaute System ist selbst leicht zu transportieren, aber es ist viel einfacher, sich darauf zu verlassen, als herauszufinden, welche Variante echoSie haben.)

zwol
quelle
1
Das ist ein bisschen verworren. Die übliche portable Methode zum Testen, ob eine Zeichenfolge bestimmte Zeichen enthält, ist mitcase .
Gilles
@ Gilles Ich glaube nicht, dass es möglich ist, ein caseGlob zu schreiben , um eine Zeichenfolge zu erkennen, die entweder leer ist oder nur Leerzeichen enthält . (Mit einem regulären Ausdruck wäre es einfach, aber er caseführt keine regulären Ausdrücke aus. Das Konstrukt in Ihrer Antwort funktioniert nicht, wenn Sie sich für Zeilenumbrüche interessieren.)
zwol
1
In meiner Antwort habe ich als Beispiel Leerzeichen angegeben, weil die Frage danach lautete. Es ist ebenso einfach für Leerzeichen zu testen: case $param in *[![:space:]]*) …. Wenn Sie nach IFSZeichen suchen möchten , können Sie das Muster verwenden *[$IFS]*.
Gilles
1
@mikeserv In einem wirklich ernsthaften defensiven Skript setzen Sie IFS <space><tab><newline>am Anfang explizit auf und berühren es dann nie wieder. Ich dachte, das wäre eine Ablenkung.
29.
1
In Bezug auf Variablen in Mustern denke ich, dass dies in allen Bourne-Versionen und allen POSIX-Shells funktioniert. Es würde zusätzlichen Code erfordern, um Fallmuster zu erkennen und die Variablenerweiterung zu deaktivieren, und ich kann mir keinen Grund dafür vorstellen. Ich habe ein paar Beispiele in meinem, .profiledie von vielen Borowski-Muscheln ausgeführt wurden. Natürlich [$IFS]wird nicht getan, was beabsichtigt war, wenn IFSbestimmte nicht standardmäßige Werte (leer (oder nicht gesetzt), enthaltend ], beginnend mit !oder ^) vorliegen .
Gilles
4
if [[ -n "${variable_name/[ ]*\n/}" ]]
then
    #execute if the the variable is not empty and contains non space characters
else
    #execute if the variable is empty or contains only spaces
fi
Guru
quelle
Dadurch werden alle Leerzeichen entfernt, nicht nur einzelne Leerzeichen, oder? danke
Alexander Mills
0

Um zu testen, ob die Variable leer ist oder Leerzeichen enthält, können Sie auch diesen Code verwenden:

${name:?variable is empty}
Pratik
quelle
2
Ich denke, Ihre Antwort ist herabgestuft, weil das "oder Leerzeichen enthalten" nicht wahr ist.
Bernhard
sei vorsichtig - das tötet die aktuelle Shell. Besser in eine (: $ {subshell?}). Außerdem - das schreibt an stderr - möchten Sie wahrscheinlich umleiten. Und das Wichtigste ist, dass der tatsächliche Wert der Variablen ermittelt wird, wenn er nicht nicht gesetzt oder null ist. Wenn es dort einen Befehl gibt, haben Sie ihn einfach ausgeführt. Am besten führen Sie diesen Test durch :.
mikeserv
Wie wird es Shell töten. und du kannst dies auch testen, erst definieren, name=aaaadann diesen Befehl ausführen, echo ${name:?variable is empty}er gibt den Wert des Variablennamens aus und jetzt diesen Befehl, echo ${name1:?variable is empty}er gibt -sh: name1 aus: Variable ist leer
pratik
Ja - echo ${name:?variable is empty}wird entweder als $nameNicht-Null-Wert ausgewertet oder die Shell wird beendet. So aus dem gleichen Grund Sie nicht tun , prompt > $randomvarweder sollten Sie ${var?}- wenn ein Befehl gibt es dort, ist es wahrscheinlich zu laufen gehen - und Sie wissen nicht , was es ist. Eine interaktive Shell muss nicht beendet werden. Wenn Sie dennoch einige Tests sehen möchten, gibt es hier viele . . Dieser ist nicht ganz so lang ...
mikeserv
0

Um zu testen, ob eine Variable nicht gesetzt ist, leer ist (einen Nullwert hat) oder nur Leerzeichen enthält:

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"

Erläuterung:

  • Die Expansion von ${param%%[! ]*}entfernt sich vom ersten Nichtraum bis zum Ende.
  • Das lässt nur die führenden Räume.
  • Die nächste Erweiterung entfernt die führenden Leerzeichen aus der Variablen.
  • Wenn das Ergebnis leer ist, war die Variable von Anfang an leer.
  • Wenn die Variable nicht leer war, wurde sie durch Entfernen der führenden Leerzeichen leer.
  • Wenn das Ergebnis leer ist -z, wird ein Echo ausgegeben empty.

Das obige ist POSIX-kompatibel und kann in allen Bourne-Nachkommen ausgeführt werden.

Wenn Sie dies auch auf Registerkarten ausweiten möchten, fügen Sie eine explizite Registerkarte hinzu:

 [ -z "${param#"${param%%[!     ]*}"}" ] && echo "empty"

oder verwenden Sie eine Variable mit den Zeichen, die Sie entfernen müssen:

 var=$' \t'   # in ksh, bash, zsh
 [ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty"

oder Sie können einen POSIX "Zeichenklassenausdruck" verwenden (Leerzeichen bedeutet Leerzeichen und Tabulator):

 [ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty"

Aber das wird in mksh, lksh und posh scheitern.

Isaac
quelle
-1

Versuche dies:

$ [[ -z \`echo $n\` ]] && echo zero

zero
Hugo
quelle