Test per stringa di lunghezza diversa da zero in Bash: [-n “$ var”] o [“$ var”]


182

Ho visto gli script Bash testare una stringa di lunghezza diversa da zero in due modi diversi. La maggior parte degli script utilizza l' -nopzione:

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

Ma l'opzione -n ​​non è davvero necessaria:

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

Qual è il modo migliore?

Allo stesso modo, qual è il modo migliore per testare a lunghezza zero:

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

o

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

Risposte:


394

Modifica: questa è una versione più completa che mostra più differenze tra [(aka test) e [[.

La tabella seguente mostra che se una variabile viene quotata o meno, se si usano parentesi singole o doppie e se la variabile contiene solo uno spazio sono le cose che influenzano se l'uso di un test con o senza -n/-zè adatto per il controllo di una variabile.

     | 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

Se vuoi sapere se una variabile ha una lunghezza diversa da zero, esegui una delle seguenti operazioni:

  • cita la variabile tra parentesi singole (colonna 2a)
  • usa -ne cita la variabile tra parentesi singole (colonna 4a)
  • utilizzare parentesi doppie con o senza virgoletta e con o senza -n(colonne 1b - 4b)

Si noti nella colonna 1a che inizia dalla riga "due" che il risultato indica che [sta valutando il contenuto della variabile come se facessero parte dell'espressione condizionale (il risultato corrisponde all'asserzione implicita dalla "T" o "F" in la colonna della descrizione). Quando [[viene utilizzato (colonna 1b), il contenuto variabile viene visto come una stringa e non valutato.

Gli errori nelle colonne 3a e 5a sono causati dal fatto che il valore della variabile include uno spazio e la variabile non è quotata. Ancora una volta, come mostrato nelle colonne 3b e 5b, [[valuta il contenuto della variabile come una stringa.

Di conseguenza, per i test per stringhe di lunghezza zero, le colonne 6a, 5b e 6b mostrano i modi corretti per farlo. Si noti inoltre che uno qualsiasi di questi test può essere annullato se la negazione mostra un intento più chiaro rispetto all'utilizzo dell'operazione opposta. Ad esempio: if ! [[ -n $var ]].

Se stai utilizzando [, la chiave per assicurarti di non ottenere risultati imprevisti è la citazione della variabile. Usando [[, non importa.

I messaggi di errore, che vengono eliminati, sono "operatore unario previsto" o "operatore binario previsto".

Questo è lo script che ha prodotto la tabella sopra.

#!/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

1
Grazie! Ho deciso di adottare lo stile di "citare la variabile tra parentesi singole (colonna 2a)" IMO, il -n aggiunge solo rumore e diminuisce la leggibilità. Allo stesso modo, per i test per lunghezza zero o non impostato, userò [! "$ var"] invece di [-z "$ var"].
AllenHalsey,

2
Quindi il tuo grafico per ["vs [-n"(la prima domanda del PO) mostra che sono completamente equivalenti, giusto?
Piani cottura

1
@hobs: Sì, e quale utilizzare dipende da quale è più chiaro. Noterai anche che il loro equivalente quando si utilizza il modulo a doppia parentesi che è preferito quando si utilizza Bash.
In pausa fino a nuovo avviso.

1
Quindi, per riassumere il tuo fantastico tavolo, il modo più chiaro ("migliore") per testare stringhe non nulle è[["
piano cottura

22

È meglio usare il più potente per [[ quanto riguarda Bash.

Casi usuali

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

I due costrutti sopra sembrano puliti e leggibili. Dovrebbero bastare nella maggior parte dei casi.

Nota che non abbiamo bisogno di citare le espansioni variabili all'interno in [[quanto non vi è alcun pericolo di divisione delle parole e globbing .

Per evitare i lievi reclami di shellcheck[[ $var ]] e [[ ! $var ]], potremmo usare l' -nopzione.

Casi rari

Nel raro caso in cui dovessimo fare una distinzione tra "essere impostato su una stringa vuota" vs "non essere impostato affatto", potremmo usare questi:

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

Possiamo anche usare il -vtest:

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

Post e documentazione correlati

Ci sono molti post relativi a questo argomento. Eccone alcuni:


9

Ecco alcuni altri test

Vero se la stringa non è vuota:

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

Vero se la stringa è vuota:

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

Questo non risponde alla domanda di OP su quale sia il modo migliore.
codeforester

0

La risposta corretta è la seguente:

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

Nota l'uso di [[...]], che gestisce correttamente la citazione delle variabili per te.


2
Perché usarlo -nquando non è veramente necessario in Bash?
codeforester,

0

Un modo alternativo e forse più trasparente di valutare una variabile d'ambiente vuota è usare ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi

10
Questa è molto vecchia scuola dai tempi della conchiglia di Bourne. Non perpetuare questa vecchia abitudine. bashè uno strumento più nitido rispetto ai suoi predecessori.
martedì

2
Non hai nemmeno bisogno di questo con shell pre-bash, se stai evitando la sintassi che lo standard POSIX contrassegna esplicitamente obsolescente. [ "$ENV_VARIABLE" != "" ]funzionerà su ogni shell con testun'implementazione conforme a POSIX - non solo bash, ma ash / dash / ksh / etc.
Charles Duffy,

... vale a dire, non usare -ao -ocombinare i test ma invece utilizzare [ ... ] && [ ... ]o [ ... ] || [ ... ]e gli unici casi angolari che possono applicarsi all'uso di dati di variabili arbitrarie in un test sono chiusi in modo inequivocabile.
Charles Duffy

-2

Uso case/esac per testare:

case "$var" in
  "") echo "zero length";;
esac

12
No per favore. Questo non è ciò per cui caseè destinato.
martedì

2
casefunziona meglio quando ci sono più di due alternative.
codeforester

casenon è destinato a questo tipo di utilizzo.
Luis Lavaire
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.