Teste une chaîne de longueur différente de zéro dans Bash: [-n “$ var”] ou [“$ var”]

182

J'ai vu des scripts Bash tester une chaîne de longueur non nulle de deux manières différentes. La plupart des scripts utilisent l' -noption:

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

Mais l'option -n n'est pas vraiment nécessaire:

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

Quelle est la meilleure façon?

De même, quelle est la meilleure façon de tester la longueur nulle:

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

ou

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

Réponses:

394

Edit: Ceci est une version plus complète qui montre plus de différences entre [(aka test) et [[.

Le tableau suivant montre que le fait qu'une variable soit entre guillemets ou non, que vous utilisiez des crochets simples ou doubles et si la variable ne contienne qu'un espace sont les éléments qui déterminent si l'utilisation d'un test avec ou sans -n/-zconvient pour vérifier une variable.

     | 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

Si vous voulez savoir si une variable est de longueur différente de zéro, effectuez l'une des opérations suivantes:

  • citer la variable entre parenthèses simples (colonne 2a)
  • utiliser -net citer la variable entre parenthèses simples (colonne 4a)
  • utiliser des crochets doubles avec ou sans guillemets et avec ou sans -n(colonnes 1b - 4b)

Notez dans la colonne 1a commençant à la ligne étiquetée «deux» que le résultat indique qu'il [évalue le contenu de la variable comme s'il faisait partie de l'expression conditionnelle (le résultat correspond à l'assertion impliquée par le «T» ou «F» dans la colonne de description). Quand [[est utilisé (colonne 1b), le contenu de la variable est vu comme une chaîne et n'est pas évalué.

Les erreurs dans les colonnes 3a et 5a sont causées par le fait que la valeur de la variable comprend un espace et que la variable n'est pas entre guillemets. Encore une fois, comme indiqué dans les colonnes 3b et 5b, [[évalue le contenu de la variable sous forme de chaîne.

De même, pour les tests de chaînes de longueur nulle, les colonnes 6a, 5b et 6b montrent les bonnes façons de le faire. Notez également que n'importe lequel de ces tests peut être annulé si la négation montre une intention plus claire que l'utilisation de l'opération opposée. Par exemple: if ! [[ -n $var ]].

Si vous utilisez [, la clé pour vous assurer que vous n'obtenez pas de résultats inattendus est de citer la variable. En utilisant [[, cela n'a pas d'importance.

Les messages d'erreur, qui sont supprimés, sont "opérateur unaire attendu" ou "opérateur binaire attendu".

C'est le script qui a produit le tableau ci-dessus.

#!/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
Suspendu jusqu'à nouvel ordre.
la source
1
Merci! J'ai décidé d'adopter le style "citer la variable entre parenthèses simples (colonne 2a)" IMO, le -n ajoute juste du bruit et diminue la lisibilité. De même, pour tester la longueur nulle ou non définie, j'utiliserai [! "$ var"] au lieu de [-z "$ var"].
AllenHalsey
2
Donc, votre graphique pour ["vs [-n"(la première question du PO) montre qu'ils sont complètement équivalents, non?
plaques de cuisson
1
@hobs: Oui, et lequel utiliser dépend de ce qui est le plus clair. Vous remarquerez également que leur équivalent lors de l'utilisation de la forme à double crochet qui est préférable lors de l'utilisation de Bash.
Suspendu jusqu'à nouvel ordre.
1
Donc, pour résumer votre superbe table, le moyen le plus clair ("meilleur") de tester les chaînes non nulles est[["
hobs
22

Il vaut mieux utiliser le plus puissant [[ en ce qui concerne Bash.

Cas habituels

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

Les deux constructions ci-dessus semblent propres et lisibles. Ils devraient suffire dans la plupart des cas.

Notez que nous n'avons pas besoin de citer les extensions de variables à l'intérieur [[car il n'y a aucun risque de fractionnement et de globalisation des mots .

Pour éviter les plaintes douces de shellcheck à propos de [[ $var ]]et [[ ! $var ]], nous pourrions utiliser l' -noption.

Cas rares

Dans le cas rare où nous devions faire une distinction entre "être défini sur une chaîne vide" et "ne pas être défini du tout", nous pourrions utiliser ceux-ci:

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

Nous pouvons également utiliser le -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

Articles et documentation connexes

Il existe de nombreux articles liés à ce sujet. Voici quelques-uns:

codeforester
la source
9

Voici quelques tests supplémentaires

Vrai si la chaîne n'est pas vide:

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

Vrai si la chaîne est vide:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"
Steven Penny
la source
Cela ne répond pas à la question d'OP de savoir quel est le meilleur moyen.
codeforester
0

La bonne réponse est la suivante:

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

Notez l'utilisation de [[...]], qui gère correctement la citation des variables pour vous.

user2592126
la source
2
Pourquoi l'utiliser -nquand ce n'est pas vraiment nécessaire dans Bash?
codeforester
0

Une manière alternative et peut-être plus transparente d'évaluer une variable d'environnement vide consiste à utiliser ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi
user1310789
la source
10
C'est très old-school de l'époque de Bourne Shell. Ne perpétuez pas cette vieille habitude. bashest un outil plus pointu que ses prédécesseurs.
tbc0 du
2
Vous n'en avez même pas besoin avec les shells pré-bash, si vous évitez la syntaxe que la norme POSIX marque explicitement obsolescente. [ "$ENV_VARIABLE" != "" ]fonctionnera sur tous les shell avec une testimplémentation compatible POSIX - pas seulement bash, mais ash / dash / ksh / etc.
Charles Duffy
... c'est-à-dire, n'utilisez pas -aou -opour combiner des tests mais utilisez plutôt [ ... ] && [ ... ]ou [ ... ] || [ ... ]et les seuls cas de coin qui peuvent s'appliquer à l'utilisation des données de variables arbitraires dans un test sont fermés sans ambiguïté.
Charles Duffy
-2

Utilisez case/esacpour tester:

case "$var" in
  "") echo "zero length";;
esac
ghostdog74
la source
12
Non je t'en prie. Ce n'est pas ce à quoi caseça sert.
tbc0
2
casefonctionne mieux lorsqu'il existe plus de deux alternatives.
codeforester
casen'est pas destiné à ce type d'utilisation.
Luis Lavaire