Test auf Zeichenfolge ungleich Null in Bash: [-n “$ var”] oder [“$ var”]

181

Ich habe gesehen, wie Bash-Skripte auf zwei verschiedene Arten auf eine Zeichenfolge ungleich Null getestet wurden. Die meisten Skripte verwenden die -nOption:

#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
  # Do something when var is non-zero length
fi

Die Option -n wird jedoch nicht wirklich benötigt:

# Without the -n option
if [ "$var" ]; then
  # Do something when var is non-zero length
fi

Welcher ist der bessere Weg?

In ähnlicher Weise ist dies der bessere Weg, um auf Nulllänge zu testen:

if [ -z "$var" ]; then
  # Do something when var is zero-length
fi

oder

if [ ! "$var" ]; then
  # Do something when var is zero-length
fi
AllenHalsey
quelle

Antworten:

392

Bearbeiten: Dies ist eine vollständigere Version, die mehr Unterschiede zwischen [(aka test) und zeigt [[.

Die folgende Tabelle zeigt, dass -n/-zes für die Überprüfung einer Variablen geeignet ist , ob eine Variable in Anführungszeichen gesetzt wird oder nicht, ob Sie einfache oder doppelte Klammern verwenden und ob die Variable nur ein Leerzeichen enthält .

     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b
     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"
-----+------------------------------------+------------------------------------
unset| false false true  false true  true | false false false false true  true
null | false false true  false true  true | false false false false true  true
space| false true  true  true  true  false| true  true  true  true  false false
zero | true  true  true  true  false false| true  true  true  true  false false
digit| true  true  true  true  false false| true  true  true  true  false false
char | true  true  true  true  false false| true  true  true  true  false false
hyphn| true  true  true  true  false false| true  true  true  true  false false
two  | -err- true  -err- true  -err- false| true  true  true  true  false false
part | -err- true  -err- true  -err- false| true  true  true  true  false false
Tstr | true  true  -err- true  -err- false| true  true  true  true  false false
Fsym | false true  -err- true  -err- false| true  true  true  true  false false
T=   | true  true  -err- true  -err- false| true  true  true  true  false false
F=   | false true  -err- true  -err- false| true  true  true  true  false false
T!=  | true  true  -err- true  -err- false| true  true  true  true  false false
F!=  | false true  -err- true  -err- false| true  true  true  true  false false
Teq  | true  true  -err- true  -err- false| true  true  true  true  false false
Feq  | false true  -err- true  -err- false| true  true  true  true  false false
Tne  | true  true  -err- true  -err- false| true  true  true  true  false false
Fne  | false true  -err- true  -err- false| true  true  true  true  false false

Wenn Sie wissen möchten, ob eine Variable eine Länge ungleich Null hat, führen Sie einen der folgenden Schritte aus:

  • Geben Sie die Variable in Klammern an (Spalte 2a).
  • Verwenden -nund zitieren Sie die Variable in einfachen Klammern (Spalte 4a).
  • Verwenden Sie doppelte Klammern mit oder ohne Anführungszeichen und mit oder ohne -n(Spalten 1b - 4b)

Beachten Sie in Spalte 1a ab der Zeile mit der Bezeichnung "zwei", dass das Ergebnis angibt, dass [der Inhalt der Variablen so ausgewertet wird, als ob er Teil des bedingten Ausdrucks wäre (das Ergebnis stimmt mit der Behauptung überein, die durch "T" oder "F" in impliziert wird die Beschreibungsspalte). Bei [[Verwendung (Spalte 1b) wird der Variableninhalt als Zeichenfolge angezeigt und nicht ausgewertet.

Die Fehler in den Spalten 3a und 5a werden durch die Tatsache verursacht, dass der Variablenwert ein Leerzeichen enthält und die Variable nicht in Anführungszeichen steht. Wie in den Spalten 3b und 5b gezeigt, [[wird der Inhalt der Variablen erneut als Zeichenfolge ausgewertet.

Entsprechend zeigen die Spalten 6a, 5b und 6b für Tests für Zeichenfolgen mit der Länge Null die richtigen Möglichkeiten, dies zu tun. Beachten Sie auch, dass jeder dieser Tests negiert werden kann, wenn das Negieren eine klarere Absicht zeigt als die Verwendung der entgegengesetzten Operation. Zum Beispiel : if ! [[ -n $var ]].

Wenn Sie verwenden [, ist der Schlüssel, um sicherzustellen, dass Sie keine unerwarteten Ergebnisse erhalten, das Zitieren der Variablen. Verwenden [[ist egal.

Die Fehlermeldungen, die unterdrückt werden, sind "unärer Operator erwartet" oder "binärer Operator erwartet".

Dies ist das Skript, das die obige Tabelle erstellt hat.

#!/bin/bash
# by Dennis Williamson
# 2010-10-06, revised 2010-11-10
# for http://stackoverflow.com/q/3869072
# designed to fit an 80 character terminal

dw=5    # description column width
w=6     # table column width

t () { printf '%-*s' "$w" " true"; }
f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }

o=/dev/null

echo '     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b'
echo '     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"'
echo '-----+------------------------------------+------------------------------------'

while read -r d t
do
    printf '%-*s|' "$dw" "$d"

    case $d in
        unset) unset t  ;;
        space) t=' '    ;;
    esac

    [ $t ]        2>$o  && t || f
    [ "$t" ]            && t || f
    [ -n $t ]     2>$o  && t || f
    [ -n "$t" ]         && t || f
    [ -z $t ]     2>$o  && t || f
    [ -z "$t" ]         && t || f
    echo -n "|"
    [[ $t ]]            && t || f
    [[ "$t" ]]          && t || f
    [[ -n $t ]]         && t || f
    [[ -n "$t" ]]       && t || f
    [[ -z $t ]]         && t || f
    [[ -z "$t" ]]       && t || f
    echo

done <<'EOF'
unset
null
space
zero    0
digit   1
char    c
hyphn   -z
two     a b
part    a -a
Tstr    -n a
Fsym    -h .
T=      1 = 1
F=      1 = 2
T!=     1 != 2
F!=     1 != 1
Teq     1 -eq 1
Feq     1 -eq 2
Tne     1 -ne 2
Fne     1 -ne 1
EOF
Bis auf weiteres angehalten.
quelle
1
Vielen Dank! Ich habe mich für den IMO-Stil "Anführungszeichen für die Variable in einzelnen Klammern (Spalte 2a)" entschieden. Das -n fügt nur Rauschen hinzu und verringert die Lesbarkeit. In ähnlicher Weise verwende ich zum Testen auf Nulllänge oder Nichteinstellung [! "$ var"] anstelle von [-z "$ var"].
Allen Halsey
2
Ihr Diagramm für ["vs [-n"(die erste Frage des OP) zeigt also, dass sie völlig gleichwertig sind, oder?
Kochfelder
1
@hobs: Ja, und welche zu verwenden ist, hängt davon ab, welche klarer ist. Sie werden auch feststellen, dass sie bei Verwendung des Doppelklammerformulars gleichwertig sind, was bei Verwendung von Bash bevorzugt wird.
Bis auf weiteres angehalten.
1
Um Ihre fantastische Tabelle zusammenzufassen, ist die klarere ("bessere") Möglichkeit, auf Nicht-Null-Zeichenfolgen zu testen,[["
Kochfelder
21

Es ist besser , das zu verwenden , leistungsfähigeren [[ bis Bash betrifft.

Übliche Fälle

if [[ $var ]]; then   # var is set and it is not empty
if [[ ! $var ]]; then # var is not set or it is set to an empty string

Die beiden obigen Konstrukte sehen sauber und lesbar aus. Sie sollten in den meisten Fällen ausreichen.

Beachten Sie, dass wir die Variablenerweiterungen im Inneren nicht zitieren müssen, [[da keine Gefahr der Wortteilung und des Globbing besteht .

Um die weichen Beschwerden von Shellcheck über [[ $var ]]und zu vermeiden [[ ! $var ]], könnten wir die -nOption verwenden.

Seltene Fälle

In dem seltenen Fall, dass wir zwischen "auf eine leere Zeichenfolge gesetzt" und "überhaupt nicht gesetzt" unterscheiden müssen, könnten wir diese verwenden:

if [[ ${var+x} ]]; then           # var is set but it could be empty
if [[ ! ${var+x} ]]; then         # var is not set
if [[ ${var+x} && ! $var ]]; then # var is set and is empty

Wir können den -vTest auch verwenden :

if [[ -v var ]]; then             # var is set but it could be empty
if [[ ! -v var ]]; then           # var is not set
if [[ -v var && ! $var ]]; then   # var is set and is empty
if [[ -v var && -z $var ]]; then  # var is set and is empty

Verwandte Beiträge und Dokumentation

Es gibt viele Beiträge zu diesem Thema. Hier sind ein paar:

Codeforester
quelle
9

Hier sind einige weitere Tests

True, wenn der String nicht leer ist:

[ -n "$var" ]
[[ -n $var ]]
test -n "$var"
[ "$var" ]
[[ $var ]]
(( ${#var} ))
let ${#var}
test "$var"

True, wenn die Zeichenfolge leer ist:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"
Steven Penny
quelle
Dies beantwortet nicht die Frage von OP, was der bessere Weg ist.
Codeforester
0

Die richtige Antwort lautet wie folgt:

if [[ -n $var ]] ; then
  blah
fi

Beachten Sie die Verwendung von [[...]], die das Zitieren der Variablen für Sie korrekt handhabt.

user2592126
quelle
2
Warum verwenden, -nwenn es in Bash nicht wirklich benötigt wird?
Codeforester
0

Eine alternative und möglicherweise transparentere Methode zur Bewertung einer leeren Umgebungsvariablen ist die Verwendung von ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi
user1310789
quelle
10
Dies ist sehr altmodisch aus den Tagen der Bourne Shell. Behalte diese alte Gewohnheit nicht bei. bashist ein schärferes Werkzeug als seine Vorgänger.
tbc0
1
Sie benötigen dies nicht einmal für Pre-Bash-Shells, wenn Sie die Syntax vermeiden, die der POSIX-Standard explizit als veraltet markiert. [ "$ENV_VARIABLE" != "" ]funktioniert auf jeder Shell mit einer POSIX-kompatiblen testImplementierung - nicht nur bash, sondern ash / dash / ksh / etc.
Charles Duffy
... das heißt, verwenden -aoder -okombinieren Sie keine Tests, sondern verwenden Sie [ ... ] && [ ... ]oder [ ... ] || [ ... ]und die einzigen Eckfälle, die für die Verwendung der Daten beliebiger Variablen in einem Test gelten können, sind eindeutig geschlossen.
Charles Duffy
-2

Verwenden Sie case/esaczum Test:

case "$var" in
  "") echo "zero length";;
esac
Ghostdog74
quelle
12
Nein, bitte. Dafür ist nicht casegedacht.
TBC0
2
casefunktioniert am besten, wenn es mehr als zwei Alternativen gibt.
Codeforester
caseist nicht für diese Art der Verwendung vorgesehen.
Luis Lavaire