Comment puis-je tester si une variable est vide ou ne contient que des espaces?

240

La syntaxe bash suivante vérifie si elle paramn'est pas vide:

 [[ !  -z  $param  ]]

Par exemple:

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

Pas de sortie et c'est bien.

Mais quand paramest vide sauf pour un (ou plusieurs) espace, le cas est différent:

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

"Je ne suis pas zéro" est sorti.

Comment puis-je modifier le test pour considérer les variables qui ne contiennent que des espaces, vides?

maihabunash
la source
4
De man test: -z STRING - the length of STRING is zero. Si vous souhaitez supprimer tous les espaces $param, utilisez${param// /}
dchirikov le
6
WOW, il n'y a pas une trim()fonction simple intégrée à aucun * nix du tout? Autant de trucs différents pour réaliser quelque chose d'aussi simple ...
ADTC
6
@ADTC $ (la chaîne de caractères / ^ \ s + | \ s + $ // g '<<< $ string) me semble assez simple. Pourquoi réinventer la roue?
jbowman
3
Parce qu'il pourrait être plus brillant
Inversus
1
Juste en notant que cela [[ ! -z $param ]]équivaut à test ! -z $param.
Noah Sussman

Réponses:

292

Tout d'abord, notez que le -ztest est explicitement pour:

la longueur de la chaîne est zéro

Autrement dit, une chaîne ne contenant que des espaces ne doit pas être vraie sous -z, car elle a une longueur non nulle.

Ce que vous voulez, c'est supprimer les espaces de la variable à l'aide de l' extension de paramètre de remplacement de modèle :

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

Cela développe la paramvariable et remplace toutes les correspondances du modèle (un seul espace) par rien. Ainsi, une chaîne contenant uniquement des espaces sera développée en une chaîne vide.


La façon dont cela fonctionne est de ${var/pattern/string}remplacer le premier match le plus long de patternavec string. Quand patterncommence par /(comme ci-dessus), il remplace toutes les correspondances. Comme le remplacement est vide, nous pouvons omettre le final /et la stringvaleur:

$ {paramètre / modèle / chaîne}

Le modèle est développé pour produire un modèle comme dans l’extension de nom de fichier. Le paramètre est développé et la correspondance la plus longue du motif avec sa valeur est remplacée par une chaîne . Si motif commence par '/', toutes les correspondances de motif sont remplacées par une chaîne . Normalement, seul le premier match est remplacé. ... Si string est null, les correspondances de modèle sont supprimées et le modèle / suivant peut être omis.

Après tout cela, on finit par ${param// }supprimer tous les espaces.

Notez que, bien qu’elle soit présente dans ksh(où elle a son origine), zshet bashque cette syntaxe n’est pas POSIX, elle ne doit pas être utilisée dans les shscripts.

Michael Homer
la source
1
utiliser sedils ont dit ... juste echola variable ils ont dit ...
mikezter
1
Hmm. Si c'est le ${var/pattern/string}cas, ne devriez-vous pas laisser [[ -z ${param/ /} ]]l'espace entre les barres obliques être le motif et rien après la chaîne?
Jesse Chisholm
@JesseChisholm "Comme le remplacement est vide, nous pouvons omettre la valeur finale / et la valeur de la chaîne"; "Si la chaîne est nulle, les correspondances de modèle sont supprimées et le modèle / suivant peut être omis."
Michael Homer
6
@MichaelHomer La réponse est [[ -z "${param// }" ]]sûre, le patternest vide et le replacement stringest un seul espace. AH! Je le vois maintenant if pattern begins with a slashj'ai raté cette partie. le motif est donc / et la chaîne est laissée et donc vide. Merci.
Jesse Chisholm
note bash: si vous exécutez set -o nounset (set -u) et que param est non défini (plutôt que null ou rempli avec de l'espace), cela générera une erreur de variable non liée. (set + u; .....) exempterait ce test.
Brian Chrisman
31

Le moyen le plus simple de vérifier qu’une chaîne ne contient que des caractères d’un ensemble autorisé consiste à vérifier la présence de caractères non autorisés. Ainsi, au lieu de vérifier si la chaîne contient uniquement des espaces, vérifiez si la chaîne contient un caractère autre que l'espace. En bash, ksh ou zsh:

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

«Ne contient que des espaces» inclut le cas d'une variable vide (ou non définie).

Vous voudrez peut-être tester n'importe quel caractère d'espacement. Utilisez-le [[ $param = *[^[:space:]]* ]]pour utiliser les paramètres régionaux ou la liste explicite de caractères d'espaces que vous souhaitez tester, par exemple [[ $param = *[$' \t\n']* ]]pour tester l'espace, la tabulation ou la nouvelle ligne.

Faire correspondre une chaîne avec un motif à l' =intérieur [[ … ]]est une extension ksh (également présente dans bash et zsh). Dans n'importe quel style Bourne / POSIX, vous pouvez utiliser la caseconstruction pour faire correspondre une chaîne à un modèle. Notez que les modèles de shell standard utilisent !pour nier un jeu de caractères, plutôt que ^dans la plupart des syntaxes d'expression régulières.

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

Pour tester les caractères d'espacement, la $'…'syntaxe est spécifique à ksh / bash / zsh; vous pouvez insérer ces caractères dans votre script de manière littérale (notez qu'un retour à la ligne doit être placé entre guillemets, car la barre oblique inverse + le trait nouveau est réduit à néant), ou les générer, par exemple

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac
Gilles
la source
C'est presque excellent - mais, pour le moment, cela ne répond pas à la question posée. Il peut actuellement vous indiquer la différence entre une variable shell non définie ou vide et une variable ne contenant que des espaces. Si vous acceptez ma suggestion, vous ajouterez le formulaire d’expansion des paramètres qui n’a pas encore été converti par une autre réponse qui teste explicitement le réglage d’une variable du shell (je veux dire) ${var?not set}: la réussite de ce test et sa survie garantiraient qu’une variable correspondant à votre *) caseproduirait un résultat définitif. répondre, par exemple.
mikeserv
Correction - cela, en fait, serait répondre à la question posée à l' origine, mais il a depuis été modifié de telle sorte qu'il change fondamentalement le sens de la question et donc invalident votre réponse.
mikeserv
Vous avez besoin [[ $param = *[!\ ]* ]]de 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

Pour différencier les espaces vides , non vides , vides et non définis :

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

[:blank:]est pour les caractères d'espacement horizontal (espace et tabulation en ASCII, mais il y en a probablement quelques autres dans vos paramètres régionaux; certains systèmes incluent l'espace insécable (le cas échéant), d'autres non). Si vous souhaitez également utiliser des caractères d'espacement vertical (comme une nouvelle ligne ou un saut de formulaire), remplacez-le [:blank:]par [:space:].

Stéphane Chazelas
la source
POSIXly est idéal car il fonctionnera avec le tiret (sans doute meilleur).
Ken Sharp
zshsemble avoir un problème avec [![:blank:]], elle :best un modificateur invalide. Dans shet kshimiter, il soulève événement non trouvé pour[
lundi
@cuonglm, qu'as-tu essayé? var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'est ok pour moi. L'événement non trouvé ne concernerait que des coques interactives.
Stéphane Chazelas
@ StéphaneChazelas: Ah bon, j'ai essayé avec des coques interactives. Le :bmodificateur invalide est un peu étrange.
jeudi
1
@FranklinYu, sur OS / X shest bashconstruit avec --enable-xpg-echo-defaultet de --enable-strict-posix-defaultmanière à être conforme à Unix (ce qui implique la echo '\t'sortie d'un onglet).
Stéphane Chazelas
4

La seule raison qui reste pour écrire un script shell plutôt qu'un script dans un bon langage de script est si la portabilité extrême est une préoccupation primordiale. L'héritage /bin/shest la seule chose dont vous pouvez être certain, mais Perl, par exemple, est plus susceptible d'être disponible sur plusieurs plates-formes que Bash. Par conséquent, n'écrivez jamais de scripts shell utilisant des fonctionnalités qui ne sont pas vraiment universelles - et gardez à l'esprit que plusieurs fournisseurs Unix propriétaires ont gelé leur environnement shell avant POSIX.1-2001 .

Il existe un moyen portable de faire ce test, mais vous devez utiliser tr:

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

(La valeur par défaut $IFS, commodément, est un espace, un onglet et une nouvelle ligne.)

(La fonction printfintégrée est elle-même assez portable, mais il est beaucoup plus simple de s'y fier que de déterminer quelle variante echovous avez.)

zwol
la source
1
C'est un peu compliqué. Le moyen portable habituel de tester si une chaîne contient certains caractères est aveccase .
Gilles
@ Gilles Je ne pense pas qu'il soit possible d'écrire un caseglob pour détecter une chaîne vide ou contenant uniquement des caractères d'espacement. (Ce serait facile avec une expression rationnelle, mais ce casen'est pas le cas. La construction de votre réponse ne fonctionnera pas si vous vous souciez des nouvelles lignes.)
mardi,
1
J'ai donné des espaces à titre d'exemple dans ma réponse parce que c'est ce que la question demandait. Il est tout aussi facile de tester les caractères blancs: case $param in *[![:space:]]*) …. Si vous souhaitez tester les IFScaractères, vous pouvez utiliser le modèle *[$IFS]*.
Gilles
1
@mikeserv Dans un script très sérieusement défensif, vous définissez explicitement IFS <space><tab><newline>au début et ne le touchez plus jamais. Je pensais que ce serait une distraction.
Zwol
1
En ce qui concerne les variables dans les modèles, je pense que cela fonctionne dans toutes les versions de Bourne et dans tous les shells POSIX; il faudrait du code supplémentaire pour détecter les modèles de cas et désactiver l’extension variable et je ne vois aucune raison de le faire. J'en ai quelques exemples .profilequi ont été exécutés par de nombreux obus Bourne. Bien sûr [$IFS], ne fera pas ce qui était prévu si IFScertaines valeurs autres que celles par défaut (vide (ou non définie), contenant ], commençant par !ou ^) sont présentes.
Gilles le
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
Gourou
la source
cela supprime tout caractère d'espace blanc, pas seulement un seul espace, non? merci
Alexander Mills
0

Pour tester si la variable est vide ou contient des espaces, vous pouvez également utiliser ce code:

${name:?variable is empty}
pratik
la source
2
Je pense que votre réponse est rejetée parce que "ou contenir des espaces" n'est pas vrai.
Bernhard
soyez prudent - cela tue le shell actuel. Mieux vaut le mettre dans un (: $ {subshell?}). En outre - cela écrira à stderr - vous voulez probablement une redirection. Et le plus important est l’ évaluation de la valeur réelle de la variable si elle n’est pas non définie ou nulle. S'il y a une commande à l'intérieur, vous venez de l'exécuter. Il est préférable d'exécuter ce test :.
mikeserv
comment il va tuer shell. et u peut aussi tester, d' abord définir name=aaaapuis exécutez cette commande , echo ${name:?variable is empty}il affiche la valeur de nom de variable et maintenant exécuter cette commande , echo ${name1:?variable is empty}il imprimera -SH: nom1: variable est vide
Pratik
Oui - echo ${name:?variable is empty}évaluera soit la $namevaleur non-nulle de s ou tuera le shell. Donc, pour la même raison, vous ne le ferez pas prompt > $randomvarnon plus ${var?}- s'il y a une commande, elle va probablement s'exécuter - et vous ne savez pas ce que c'est. Un shell interactif n'a pas à sortir - c'est pourquoi le vôtre ne le fait pas. Néanmoins, si vous voulez voir des tests, il y en a beaucoup ici. . Celui-ci n'est pas aussi long ...
mikeserv
0

Pour tester si une variable est non définie, vide (a une valeur nulle) ou ne contient que des espaces:

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

Explication:

  • L'expansion de ${param%%[! ]*}supprime du premier non-espace à la fin.
  • Cela ne laisse que les espaces principaux.
  • L'extension suivante supprime les espaces de début de la variable.
  • Si le résultat est vide, la variable était vide pour commencer.
  • Ou, si la variable n'était pas vide, la suppression des espaces en tête la rendait vide.
  • Si le résultat est vide -z, alors écho empty.

Ce qui précède est compatible POSIX et s’applique à n’importe quel descendant de Bourne.

Si vous souhaitez étendre cela aussi aux onglets, incluez un onglet explicite:

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

ou utilisez une variable avec les caractères à supprimer:

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

ou vous pouvez utiliser une "expression de classe de caractère" POSIX (vide signifie espace et tabulation):

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

Mais cela échouera dans mksh, lksh et chic.

Isaac
la source
-1

Essaye ça:

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

zero
Hugo
la source