10 votes

En utilisant un joker dans une condition pour correspondre au début d'une chaîne

Essayez de faire en sorte qu'une variable rencontre une chaîne de texte dont la longueur varie. La partie de la chaîne qui importe est les premiers caractères.

var1 = hello
if [ $var1 == hello ]; alors
    echo réussi
fi

Ou

var1 = hello
if [ $var1 == h*o ]; alors
    echo réussi
fi

Sorties: réussi

Mais comme je ne me soucie que des trois premiers caractères environ, cela semble logique, mais ne fonctionne pas :

var1 = hello
if [ $var1 == hel* ]; alors
    echo réussi
fi

Sorties : -bash: [: trop d'arguments

Puisque je me préoccupe seulement des premiers caractères, je peux faire :

var1 = hello
si [ ${var1:0:3} == hel ]; alors
    echo réussi
fi

Cela fonctionnerait, mais je cherche une explication sur pourquoi j'obtiens cette erreur et une éventuelle meilleure solution écrite.

13voto

Oliver Points 125

Lorsque vous utilisez * dans un if comme cela, il effectuera une expansion de nom de fichier. Ainsi, dans le premier cas, h*o a probablement été associé à un fichier dans votre répertoire actuel, et donc le test a réussi.

Le hel* correspondait à plusieurs fichiers, et est donc devenu trop d'arguments pour la commande if.

Essayez d'utiliser if [[ $var1 == hel* ]]; then

Les doubles crochets transforment le test en regex, et le joker * fonctionnera comme vous vous y attendez.

1voto

Ethan Wrightson Points 13

J'ai une astuce que j'utilise pour pirater en regex sans utiliser de regex bash intégré. L'exemple est pour #2. La façon dont cela fonctionne est que grep ne renvoie aucune sortie (donc une chaîne inexistante) si elle ne correspond à rien. Il y a donc deux tests, -z signifie "chaîne nulle" et -n est "a des données".

if [ -n "`echo $var1 | grep -e 'h.*o'`" ] ; then
  echo 'eau trouvée'
fi

1voto

8bittree Points 2817

[ * est une commande classique, similaire à grep, find ou cat. Vous devriez être en mesure de le trouver dans /bin. Comme c'est un programme séparé, le shell effectuera son ensemble normal d'expansions avant de transmettre ses arguments à [.

Comme cela a été mentionné, puisque vous utilisez * dans vos tests, vous obtenez des expansions de globe. Notez que même si vous utilisez des guillemets, tels que 'hel*', cela ne fonctionnera probablement pas comme vous l'espérez, car [ ne prend pas en charge les motifs. Dans le cas où h*o fonctionne, c'est probablement dû à la présence d'un fichier nommé hello dans votre répertoire actuel, et aucun autre fichier correspondant à ce motif. Si cela fonctionne sans fichier hello, vous pouvez avoir une implémentation étrange, et votre script est susceptible de ne pas fonctionner sur d'autres systèmes.

En fonction de vos besoins, il existe quelques options. Bash, Zsh et quelques autres shells ont le [[ intégré. Comme c'est une commande intégrée, elle peut donner un traitement spécial à ses arguments, y compris éviter l'expansion de glob. De plus, elle peut faire de la correspondance de motifs. Essayez

var1=hello
if [[ "$var1" = hel* ]]; then
    echo success
fi

Remarquez également l'absence de guillemets autour du motif. Sans guillemets, hel* est traité comme un motif par [[, avec des guillemets (simples ou doubles), "hel*" est traité littéralement.

Si vous avez besoin d'une compatibilité plus large, comme pour les shells sans [[, vous pouvez utiliser grep :

var1=hello
if echo "$var1" | grep -qe 'hel.*' ; then
    echo success
fi

Aucun [ ou [[ nécessaire ici, mais les guillemets autour de 'hel.*' le sont.

*Certains shells ont effectivement <code>[</code> intégré, mais c'est pour des raisons d'efficacité. Il <em>devrait</em> toujours se comporter de manière identique à l'exécutable séparé, y compris en soumettant ses arguments à la "mutilation" normale du shell.

0voto

Que diriez-vous de cela ?

if [[ "$var1" == "hel"* ]]  

Utilisez le joker à l'extérieur des guillemets lors de la comparaison de chaînes.

0voto

Mecki Points 599

[[ est spécifique à bash et l'utilisation de grep est inutilement lente car elle doit d'abord démarrer un nouveau processus. Il y a une solution beaucoup plus simple et plus rapide qui fonctionne avec chaque shell compatible POSIX :

var1=hello  
case $var1 in
    hel*) echo réussite
esac

Plus généralement, la syntaxe est :

case $var in
    ) action1 ; action2 ; action3 ; ... ; actionN ;;
    ) action1 ; action2 ; action3 ; ... ; actionN ;;
    ) action1 ; action2 ; action3 ; ... ; actionN ;;
    *) action1 ; action2 ; action2 ; ... ; actionN ;;
esac

Le premier motif qui correspond définit quelles actions effectuer. Si vous souhaitez des actions sinon qui ne s'exécutent que si aucun motif ne correspond, utilisez * comme motif car cela correspondra toujours. Vous terminez un ensemble d'actions avec ;;, qui sont facultatifs si l'instruction suivante est de toute façon esac. Vous pouvez également utiliser des sauts de ligne pour séparer les instructions, donc la syntaxe suivante est également valide :

case $var in
    ) 
       action1
       action2
       action3
       :
       actionN
    ;;

    *)
       action2
       action3
       :
       actionN
esac

Et dans les motifs, vous pouvez utiliser * et ?, ainsi que | pour "ou" et [...], comme dans [abcd] (correspond à l'une de ces 4 lettres) ou comme dans [A-Z] (correspond à toutes les lettres de A à Z), ainsi que ! pour nier la correspondance. Par exemple :

for var in abc 123 3a b5 934 "" 
do
    case $var in
        "" | *[!0-9]*) echo "\"$var\" n'est PAS un entier !" ;;
        *) echo "\"$var\" est un entier."
    esac
done

Résultat :

"abc" n'est PAS un entier !
"123" est un entier.
"3a" n'est PAS un entier !
"b5" n'est PAS un entier !
"934" est un entier.
"" n'est PAS un entier !

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X