2010-01-31 7 views
460

Je voudrais vérifier si une chaîne commence par "noeud" par ex. "node001". Quelque chose commeDans bash, comment puis-je vérifier si une chaîne commence avec une certaine valeur?

if [ $HOST == user* ] 
    then 
    echo yes 
fi 

Comment puis-je faire correctement?


De plus, je dois combiner des expressions pour vérifier si l'hôte est soit « user1 » ou commence par « noeud »

if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ]; 
then 
echo yes 
fi 

> > > -bash: [: too many arguments 

Comment le faire correctement?

+5

Ne soyez pas trop tenté de combiner des expressions. Il peut sembler plus laid d'avoir deux conditions distinctes, bien que vous puissiez donner de meilleurs messages d'erreur et rendre votre script plus facile à déboguer. Aussi, j'éviterais les fonctionnalités de bash. L'interrupteur est le chemin à parcourir. – hendry

Répondre

637

Cet extrait sur le Advanced Bash Scripting Guide dit:

# The == comparison operator behaves differently within a double-brackets 
# test than within single brackets. 

[[ $a == z* ]] # True if $a starts with a "z" (wildcard matching). 
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching). 

Vous l'aviez presque correcte; vous avez besoin de doubles parenthèses, pas de crochets simples.


En ce qui concerne votre deuxième question, vous pouvez l'écrire de cette façon:

HOST=user1 
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ; 
then 
    echo yes1 
fi 

HOST=node001 
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ; 
then 
    echo yes2 
fi 

qui fera écho

yes1 
yes2 

syntaxe Bash if est difficile de se habituer à (OMI) .

+5

Pour regex voulez-vous dire [[$ a = ~^z. *]]? – JStrahl

+1

Y a-t-il une différence fonctionnelle entre '[[$ a == z *]]' et [[$ a == "z *"]] '? En d'autres termes: travaillent-ils différemment? Et que voulez-vous dire quand vous dites "$ a est égal à z *"? –

+4

Vous n'avez pas besoin du séparateur d'instructions ";" si vous mettez "then" sur sa propre ligne – Yaza

51

Vous pouvez sélectionner seulement la partie de la chaîne que vous voulez vérifier:

if [ ${HOST:0:4} = user ] 

Pour votre question de suivi, vous pouvez utiliser une OR:

if [[ $HOST == user1 || $HOST == node* ]] 
+0

Merci, Martin! J'ai ajouté au post original une nouvelle question sur la façon de combiner des expressions dans if cluase. – Tim

+6

Vous devez double-indiquer '$ {HOST: 0: 4}' –

+0

Nettoyer simple et rapide. J'irais avec cette option. –

126

Si vous utilisez une fête récente (v3 +) suggèrent bash opérateur de comparaison regex =~, à savoir

if [[ "$HOST" =~ ^user.* ]]; then 
    echo "yes" 
fi 

Pour correspondre this or that dans une utilisation regex |, à savoir

if [[ "$HOST" =~ ^user.*|^host1 ]]; then 
    echo "yes" 
fi 

Remarque - c'est 'bonne' la syntaxe d'expression régulière.

  • user* signifie use et zéro-ou-plusieurs occurrences de r, de sorte use et userrrr va correspondre.
  • user.* signifie user et zéro-ou-plus occurrences de n'importe quel caractère, donc user1, userX correspondra.
  • ^user.* signifie correspondre au modèle user.* au début de $ HOST.

Si vous n'êtes pas familier avec la syntaxe d'expression régulière, essayez de vous référer à this resource. Remarque: il est préférable de poser chaque nouvelle question sous la forme d'une nouvelle question, de rendre le stackoverflow plus ordonné et plus utile. Vous pouvez toujours inclure un lien vers une question précédente pour référence.

+0

Merci, Brabster! J'ai ajouté au post original une nouvelle question sur la façon de combiner des expressions dans if cluase. – Tim

+1

Il est dommage que la réponse acceptée ne dit rien sur la syntaxe de l'expression régulière. – CarlosRos

+0

** L'opérateur FYI Bash '= ~' ne fait que correspondre à une expression régulière quand le côté droit est UNQUOTED. Si vous citez le côté droit "Toute partie du motif peut être citée pour le forcer à correspondre comme une chaîne." ** (1.) assurez-vous de toujours mettre les expressions régulières sur la droite non-cité et (2.) Si vous stockez votre expression régulière dans une variable, assurez-vous de ne pas citer le côté droit lorsque vous faites l'expansion des paramètres. –

51

Je préfère les autres méthodes déjà affichées, mais certaines personnes aiment utiliser:

case "$HOST" in 
    user1|node*) 
      echo "yes";; 
     *) 
      echo "no";; 
esac 

Edit:

J'ai ajouté vos remplaçants à la déclaration de cas ci-dessus

Dans votre version éditée vous avez trop de parenthèses. Il devrait ressembler à ceci:

if [[ $HOST == user1 || $HOST == node* ]]; 
+0

Merci, Dennis! J'ai ajouté au post original une nouvelle question sur la façon de combiner des expressions dans if cluase. – Tim

+7

"certaines personnes aiment ...": celui-ci est plus portable sur les versions et les coques. – carlosayam

+0

Avec les instructions case, vous pouvez laisser les guillemets autour de la variable, car aucune division de mots ne se produit. Je sais que c'est inutile et incohérent, mais j'aime laisser de côté les citations pour rendre la scène locale plus attrayante. –

5
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ]; 
then 
echo yes 
fi 

ne fonctionne pas, parce que tous [, [[ et test de reconnaître la même grammaire nonrecursive. voir la section EXPRESSIONS CONDITIONNELLES dans votre page de manuel bash.

En aparté, le SUSv3 dit

La commande conditionnelle dérivée KornShell (support double [[]]) a été retiré de la description de la langue de commande shell dans une proposition précoce. Objections ont été soulevées que le vrai problème est une mauvaise utilisation de la commande test ([], et le mettre dans le shell est la mauvaise façon de résoudre le problème. Au lieu de cela, une documentation appropriée et un nouveau mot réservé shell (!) sont suffisants.

Les tests qui nécessitent plusieurs essais opérations peut être fait au niveau de la coque à l'aide des invocations individuelles de la commande et shell essai Logicals, plutôt que d'utiliser l'erreur sujettes -o drapeau de essai.

vous auriez besoin de l'écrire de cette façon, mais essai ne le supporte pas:

if [ $HOST == user1 -o $HOST == node* ]; 
then 
echo yes 
fi 

essai utilise = pour l'égalité de chaîne, plus important encore, il ne appariement de modèle de soutien.

case/esac a un bon support pour le filtrage:

case $HOST in 
user1|node*) echo yes ;; 
esac 

il a l'avantage qu'il ne dépend pas de bash, la syntaxe est portable. du Single Unix Specification, la commande Shell Langue:

case word in 
    [(]pattern1) compound-list;; 
    [[(]pattern[ | pattern] ...) compound-list;;] ... 
    [[(]pattern[ | pattern] ...) compound-list] 
esac 
+1

'[' et 'test' sont des builtins Bash ainsi que des programmes externes. Essayez 'type -a ['. –

+0

thx, édité, avec optimisme pour le mieux. –

+0

Un grand merci pour expliquer les problèmes avec le "composé ou", @just quelqu'un - cherchait précisément quelque chose comme ça! À votre santé! Note PS (non liée à OP): 'if [-z $ aa -ou -z $ bb]'; ... donne "* bash: [: -ou: opérateur binaire attendu *"; cependant 'if [-z" $ aa "-o -z" $ bb "]; ... 'passe. – sdaau

7

@OP, pour vos deux questions, vous pouvez utiliser le cas/ESAC

string="node001" 
case "$string" in 
    node*) echo "found";; 
    *) echo "no node";; 
esac 

deuxième question

case "$HOST" in 
node*) echo "ok";; 
user) echo "ok";; 
esac 

case "$HOST" in 
node*|user) echo "ok";; 
esac 

OU Bash 4.0

case "$HOST" in 
user) ;& 
node*) echo "ok";; 
esac 
+0

Notez que '; &' n'est disponible que dans Bash> = 4. –

50

J'essaie toujours de coller avec POSIX sh au lieu d'utiliser les extensions bash, car l'un des points majeurs du script est la portabilité. (En plus de connexion programmes, ne pas les remplacer)

En sh, il existe un moyen facile de vérifier une condition "is-préfixe".

case $HOST in node*) 
    your code here 
esac 

Étant donné l'âge, Arcane et crufty sh est (et bash est pas le remède: Il est plus complexe, moins cohérente et moins portable), je voudrais souligner un aspect fonctionnel très agréable: Alors que certains Les éléments de syntaxe tels que case sont intégrés, les constructions résultantes ne sont pas différentes de tout autre travail. Ils peuvent être composés de la même manière:

if case $HOST in node*) true;; *) false;; esac; then 
    your code here 
fi 

Ou encore plus court

if case $HOST in node*) ;; *) false;; esac; then 
    your code here 
fi 

Ou même plus court (juste pour présenter ! comme un élément de langage - mais cela est mauvais style maintenant)

if ! case $HOST in node*) false;; esac; then 
    your code here 
fi 

Si vous aimez être explicite, construire votre propre élément de langage:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; } 

N'est-ce pas plutôt agréable?

if beginswith node "$HOST"; then 
    your code here 
fi 

Et puisque sh est essentiellement que des emplois et des chaînes-listes (et en interne les processus, sur lesquels les emplois sont composés), nous pouvons même faire un peu de lumière programmation fonctionnelle:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; } 
checkresult() { if [ $? = 0 ]; then echo TRUE; else echo FALSE; fi; } 

all() { 
    test=$1; shift 
    for i in "[email protected]"; do 
     $test "$i" || return 
    done 
} 

all "beginswith x" x xy xyz ; checkresult # prints TRUE 
all "beginswith x" x xy abc ; checkresult # prints FALSE 

C'est élégant. Pas que je préconise d'utiliser sh pour quelque chose de sérieux - il casse trop vite sur les exigences du monde réel (pas de lambdas, donc il faut utiliser des ficelles ... Mais imbriquer des fonctions avec des chaînes n'est pas possible, les pipes ne sont pas possibles ...)

+3

+1 Non seulement il est portable, mais il est également lisible, idiomatique et élégant (pour les scripts shell). Il s'étend également naturellement à des motifs multiples; 'case $ HOST dans user01 | node *) ... ' – tripleee

+0

Y a-t-il un nom pour ce type de formatage de code? 'si cas $ HOST dans le noeud *) true ;; *) faux;; Esac Je l'ai vu ici et là, à mes yeux, il a l'air un peu froissé. –

+0

@NielsBom Je ne sais pas exactement ce que vous entendez par formatage, mais mon point de vue était que le code shell est très * composable *. Les commandes 'case' de Becaues sont des commandes, elles peuvent aller dans' if ... then'. –

22

puisque # a une signification dans bash je suis arrivé à la solution suivante.
En outre j'aime mieux emballer des ficelles avec "" pour surmonter des espaces etc.

A="#sdfs" 
if [[ "$A" == "#"* ]];then 
    echo "skip comment line" 
fi 
+0

C'était exactement ce dont j'avais besoin. Merci! –

+0

merci, je me demandais aussi comment faire correspondre une chaîne commençant par 'blah:', ressemble à ceci est la réponse! – Anentropic

18

Bien que je trouve la plupart des réponses ici tout à fait correctes, beaucoup d'entre elles contiennent des bashismes inutiles. POSIX parameter expansion vous donne tout ce que vous avez besoin:

[ "${host#user}" != "${host}" ] 

et

[ "${host#node}" != "${host}" ] 

${var#expr} des bandes le préfixe expr correspondant le plus petit de ${var} et des rentabilités. Par conséquent si ${host} fait pas commencez par user (node), ${host#user} (${host#node}) est le même que ${host}.

expr permet fnmatch() wildcards, ainsi ${host#node??} et les amis travaillent également.

-3

Une autre chose que vous pouvez faire est cat ce que vous faisant écho et le tuyau avec inline cut -c 1-1

Questions connexes