Comment comparer des chaînes dans Bash

Réponses:

1377

Utilisation de variables dans les instructions if

if [ "$x" = "valid" ]; then
  echo "x has the value 'valid'"
fi

Si vous voulez faire quelque chose quand ils ne correspondent pas, remplacez-le =par !=. Vous pouvez en savoir plus sur les opérations de chaîne et les opérations arithmétiques dans leur documentation respective.

Pourquoi utilisons-nous des guillemets $x?

Vous voulez les guillemets $x, car s'il est vide, votre script Bash rencontre une erreur de syntaxe comme indiqué ci-dessous:

if [ = "valid" ]; then

Utilisation non standard de l' ==opérateur

Notez que Bash permet ==d'être utilisé pour l'égalité avec [, mais ce n'est pas standard .

Utilisez soit le premier cas où les guillemets autour $xsont facultatifs:

if [[ "$x" == "valid" ]]; then

ou utilisez le deuxième cas:

if [ "$x" = "valid" ]; then
John Feminella
la source
12
Comment cela se rapporte-t-il à la réponse acceptée donnée dans Erreur d'opérateur inattendue ? J'ai eu la même erreur lors de l'utilisation [ "$1" == "on" ]. La modification de ["$ 1" = "on"] a résolu le problème.
Piotr Dobrogost
78
Les espaces sont nécessaires.
TAAPSogeking
6
@JohnFeminella Lors de l'écriture dans un script bash, il doit avoir un seul =et non deux.
user13107
73
il convient de noter que vous ne pouvez pas utiliser [ $x -eq "valid" ]. -eqest l'opérateur de comparaison des entiers, pas des chaînes.
craq
2
@Alex, dans quels cas (si jamais) dois-je utiliser le modèle ["x$yes" == "xyes"], c'est-à-dire le préfixe de la variable et du littéral de chaîne avec un x? Est-ce une relique d'autrefois ou est-ce vraiment nécessaire dans certaines situations?
lanoxx
142

Ou, si vous n'avez pas besoin d'une autre clause:

[ "$x" == "valid" ] && echo "x has the value 'valid'"
Marko
la source
71
Et si vous avez besoin d'une clause else et que vous voulez créer un one-liner fou: ["$ x" == "valid"] && echo "valid" || écho "invalide"
Matt White
11
@MattWhite: c'est généralement une mauvaise idée, car cela echopeut échouer.
gniourf_gniourf
1
même les pièges qui pourraient apparaître, des façons belles et élégantes à la fois de marko && MattWhite
Deko
3
@gniourf_gniourf, pas de problème, utilisez [ "$X" == "valid" ] || ( echo invalid && false ) && echo "valid" .
12431234123412341234123
4
@ 12431234123412341234123 { echo invalid && false; }est plus efficace que ( echo invalid && false ), car il évite de payer pour un sous-shell inutile.
Charles Duffy
84
a="abc"
b="def"

# Equality Comparison
if [ "$a" == "$b" ]; then
    echo "Strings match"
else
    echo "Strings don't match"
fi

# Lexicographic (greater than, less than) comparison.
if [ "$a" \< "$b" ]; then
    echo "$a is lexicographically smaller then $b"
elif [ "$a" \> "$b" ]; then
    echo "$b is lexicographically smaller than $a"
else
    echo "Strings are equal"
fi

Remarques:

  1. Les espaces entre ifet [et ]sont importants
  2. >et <sont des opérateurs de redirection donc échappez-lui avec \>et \<respectivement pour les chaînes.
Gourou
la source
6
Merci pour la comparaison de l'ordre alphabétique des chaînes
shadi
Mon problème était que l' entourait $aréellement " "dans le cadre de la valeur littérale de la chaîne, j'ai donc dû utiliser le caractère d'échappement $bpour comparer les valeurs. J'ai pu le trouver après l'exécution bash -x ./script.sh, l'indicateur -x vous permet de voir la valeur de chaque exécution et aide au débogage.
ShahNewazKhan
Notez que la comparaison de l'ordre alphabétique n'est pas normalisée POSIX, il n'est donc pas garanti de fonctionner sur des plates-formes non GNU / des shells non-bash. Seules les opérations sur pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html sont garanties d'être portables.
Charles Duffy
62

Pour comparer des chaînes avec des caractères génériques, utilisez

if [[ "$stringA" == *$stringB* ]]; then
  # Do something here
else
  # Do Something here
fi
Jeffrey L. Roberts
la source
12
Il est important que les caractères génériques ne puissent être utilisés que du côté droit! Notez également les disparus "autour des caractères génériques. (btw: +1 pour les caractères génériques!)
Scz
6
L'expansion $stringB doit être cité (et, soit dit en passant, le côté gauche n'a pas besoin d'être cité): if [[ $stringA = *"$stringB"* ]]; then.
gniourf_gniourf
j'essaye d'employer la même logique générique pour le nom de fichier dans le chemin de fichier. Mais ça ne marche pas pour moi. essayé toutes les différentes chaînes de caractères génériques fournies ici. mais ça va toujours à d'autre cas. stringA dans mon cas est un chemin de fichier / tmp / file et stringB est "file".
pluie
35

Je suis en désaccord avec l'un des commentaires sur un point:

[ "$x" == "valid" ] && echo "valid" || echo "invalid"

Non, ce n'est pas un oneliner fou

C'est juste que ça ressemble à un, hmm, les non-initiés ...

Il utilise des modèles communs comme une langue, en quelque sorte;

Et après avoir appris la langue.

En fait, c'est agréable à lire

Il s'agit d'une expression logique simple, avec une partie spéciale: l'évaluation paresseuse des opérateurs logiques.

[ "$x" == "valid" ] && echo "valid" || echo "invalid"

Chaque partie est une expression logique; le premier peut être vrai ou faux, les deux autres sont toujours vrais.

(
[ "$x" == "valid" ] 
&&
echo "valid"
)
||
echo "invalid"

Maintenant, quand il est évalué, le premier est vérifié. S'il est faux, alors le deuxième opérande de la logique et && après il n'est pas pertinent. Le premier n'est pas vrai, il ne peut donc pas être le premier et le second de toute façon.
Maintenant, dans ce cas, c'est le premier côté de la logique ou || faux, mais cela pourrait être vrai si l'autre côté - la troisième partie - est vrai.

La troisième partie sera donc évaluée - principalement en écrivant le message comme un effet secondaire. (Il a le résultat 0pour true, que nous n'utilisons pas ici)

Les autres cas sont similaires, mais plus simples - et - je le promets! sont - peuvent être - faciles à lire!
(Je n'en ai pas, mais je pense qu'être un vétéran d'UNIX avec une barbe grise y contribue beaucoup.)

Volker Siegel
la source
17
Le ... && ... || ...est généralement mal vu (désolé vétéran barbe grise d'Unix, vous vous trompez depuis tout ce temps), car il n'est pas sémantiquement équivalent à if ... then ... else .... Ne vous inquiétez pas, c'est un piège courant .
gniourf_gniourf
6
@gniourf_gniourf OP n'a pas tort - et ils ne sont probablement pas ignorants comme vous le suggérez. ... && ... || ...est un modèle parfaitement valide et un idiome bash commun. Son utilisation prescrit des connaissances préalables (ce qui peut être utile de garder à l'esprit s'il y a des débutants dans le public), mais OP a les cheveux pour prouver qu'ils savent comment éviter les couvercles de trous d'homme ouverts.
ebpa
3
@ebpa Que faire si l'instruction suivant && renvoie une valeur false, l'exécution se poursuivra jusqu'à l'instruction suivante || ? Si c'est le cas, c'est faux et c'est peut-être ce que suggère gniourf
TSG
4
Je pensais que l'écho n'était qu'un exemple. L'instruction suivant && pourrait toujours renvoyer une valeur non nulle
TSG
2
@gniourf_gniourf +1 pour avoir publié le lien vers Bash Pitfalls! Très utile!
Jaguar
21

vous pouvez également utiliser le cas d'utilisation / esac

case "$string" in
 "$pattern" ) echo "found";;
esac
ghostdog74
la source
Est-ce l'équivalence ou contient?
ytpillai
@ytpillai, c'est l'équivalence. Gardez à l'esprit que les motifs peuvent être séparés par |, avant le ). L' ininstruction est équivalente à thendans les ifinstructions. Vous pourriez faire valoir que cela fonctionne sur une liste de modèles, où chaque liste a sa propre déclaration de quoi faire, si vous venez de Python. Pas comme substring in string, mais plutôt for item in list. Utilisez a *comme dernière instruction si vous souhaitez une elsecondition. Il revient à la première rencontre.
mazunki
18

Le script suivant lit un fichier nommé "testonthis" ligne par ligne, puis compare chaque ligne avec une chaîne simple, une chaîne avec des caractères spéciaux et une expression régulière. S'il ne correspond pas, le script imprimera la ligne, sinon non.

L'espace dans Bash est tellement important. Donc, ce qui suit fonctionnera:

[ "$LINE" != "table_name" ] 

Mais ce qui suit ne le fera pas:

["$LINE" != "table_name"] 

Veuillez donc utiliser tel quel:

cat testonthis | while read LINE
do
if [ "$LINE" != "table_name" ] && [ "$LINE" != "--------------------------------" ] && [[ "$LINE" =~ [^[:space:]] ]] && [[ "$LINE" != SQL* ]]; then
echo $LINE
fi
done
Harshil
la source
Utilisez cette approche pour parcourir un fichier. Autrement dit, supprimez l'UUoC entre autres choses.
fedorqui 'SO arrête de nuire'
Ce n'est pas important à cause de bashmais parce que [c'est en fait un binaire externe (comme dans which [donne quelque chose comme /usr/bin/[)
Patrick Bergner
11

J'utiliserais probablement des correspondances d'expressions régulières si l'entrée n'a que quelques entrées valides. Par exemple, seuls "démarrer" et "arrêter" sont des actions valides.

if [[ "${ACTION,,}" =~ ^(start|stop)$ ]]; then
  echo "valid action"
fi

Notez que je minuscule la variable $ACTIONen utilisant la virgule double. Notez également que cela ne fonctionnera pas sur les versions bash trop anciennes.

steviethecat
la source
9

Exemples Bash 4+. Remarque: ne pas utiliser de guillemets entraînera des problèmes lorsque les mots contiennent des espaces, etc. Toujours citer dans Bash, IMO.

Voici quelques exemples dans Bash 4+:

Exemple 1, vérifiez «oui» dans la chaîne (insensible à la casse):

    if [[ "${str,,}" == *"yes"* ]] ;then

Exemple 2, vérifiez «oui» dans la chaîne (insensible à la casse):

    if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then

Exemple 3, vérifiez «oui» dans la chaîne (sensible à la casse):

     if [[ "${str}" == *"yes"* ]] ;then

Exemple 4, vérifiez «oui» dans la chaîne (sensible à la casse):

     if [[ "${str}" =~ "yes" ]] ;then

Exemple 5, correspondance exacte (sensible à la casse):

     if [[ "${str}" == "yes" ]] ;then

Exemple 6, correspondance exacte (insensible à la casse):

     if [[ "${str,,}" == "yes" ]] ;then

Exemple 7, correspondance exacte:

     if [ "$a" = "$b" ] ;then

Prendre plaisir.

Mike Q
la source
pour moi (mac GNU bash version 4.4.12 (1) -release x86_64-apple-darwin17.0.0), je dois utiliser if [ "$a"="$b" ]ou cela ne fonctionne pas ... ne peut pas avoir d'espaces autour des égaux
spectre
1

Je l'ai fait de cette manière qui est compatible avec Bash et Dash (sh):

testOutput="my test"
pattern="my"

case $testOutput in (*"$pattern"*)
    echo "if there is a match"
    exit 1
    ;;
(*)
   ! echo there is no coincidence!
;;esac
shades3002
la source
Quelle est la différence entre utiliser un précédent (et ne pas l'utiliser?
mazunki