Comment vérifier si une chaîne contient une sous-chaîne dans Bash

2576

J'ai une chaîne dans Bash:

string="My string"

Comment puis-je tester s'il contient une autre chaîne?

if [ $string ?? 'foo' ]; then
  echo "It's there!"
fi

??est mon opérateur inconnu. Dois-je utiliser l'écho et grep?

if echo "$string" | grep 'foo'; then
  echo "It's there!"
fi

Cela semble un peu maladroit.

davidsheldon
la source
4
Salut, si les chaînes vides sont fausses, pourquoi la considérez-vous maladroite? C'était la seule façon qui fonctionnait pour moi, malgré les solutions proposées.
ericson.cepeda
1
Vous pouvez utiliser la exprcommande ici
cli__
4
Voilà une des coquilles posix: stackoverflow.com/questions/2829613/...
sehe

Réponses:

3659

Vous pouvez également utiliser la réponse de Marcus (* caractères génériques) en dehors d'une déclaration de cas, si vous utilisez des crochets doubles:

string='My long string'
if [[ $string == *"My long"* ]]; then
  echo "It's there!"
fi

Notez que les espaces dans la chaîne d'aiguille doivent être placés entre guillemets doubles et les *caractères génériques doivent être à l'extérieur. Notez également qu'un opérateur de comparaison simple est utilisé (c'est-à-dire ==), et non l'opérateur regex =~.

Adam Bellaire
la source
127
Notez également que vous pouvez inverser la comparaison en passant simplement à! = Dans le test. Merci d'avoir répondu!
Quinn Taylor
63
@ Jonik: Vous pourriez manquer le shebang ou l'avoir comme #!/bin/sh. Essayez #!/bin/bashplutôt.
pause jusqu'à nouvel ordre.
10
Laissez un espace entre les crochets et le contenu.
Paul Price
17
Vous n'avez pas besoin de citer des variables à l'intérieur de [[]]. Cela fonctionnera aussi bien: [[$ string == $ needle ]] && echo found
Orwellophile
13
@Orwellophile Attention! Vous ne pouvez omettre que les guillemets doubles dans le premier argument de [[. Voir ideone.com/ZSy1gZ
nyuszika7h
613

Si vous préférez l'approche regex:

string='My string';

if [[ $string =~ "My" ]]
then
   echo "It's there!"
fi
Matt Tardiff
la source
2
J'ai dû remplacer une expression rationnelle egrep dans un script bash, cela a parfaitement fonctionné!
blast_hardcheese
114
L' =~opérateur recherche déjà une correspondance dans toute la chaîne; les .*ici sont étrangers. De plus, les guillemets sont généralement préférables aux contre-obliques:[[ $string =~ "My s" ]]
bukzor
16
@bukzor Quotes a cessé de fonctionner ici à partir de Bash 3.2+: tiswww.case.edu/php/chet/bash/FAQ E14) . Il est probablement préférable d'affecter à une variable (à l'aide de guillemets), puis de comparer. Comme ça:re="My s"; if [[ $string =~ $re ]]
seanf
36
Testez s'il ne contient PAS de chaîne: if [[ ! "abc" =~ "d" ]]c'est vrai.
KrisWebDev
1
Notez que l'ordre dans lequel le test a lieu est important. La sortie de la ligne de code suivante sera transmise vers l'avant 2.number=2; if [[ "1, 2, 4, 5" = *${number}* ]]; then echo forward $number; fi; if [[ *${number}* = "1, 2, 4, 5" ]]; then echo backwards $number; fi;
Douwe van der Leest
358

Je ne suis pas sûr d'utiliser une instruction if, mais vous pouvez obtenir un effet similaire avec une instruction case:

case "$string" in 
  *foo*)
    # Do stuff
    ;;
esac
Marcus Griep
la source
76
C'est probablement la meilleure solution car elle est portable pour poser des coques. (alias pas de bashismes)
technosaurus
25
@technosaurus Je trouve plutôt étrange de critiquer le "bashisme" dans une question qui n'a que le tag bash :)
PP
39
@PP Ce n'est pas tant une critique que la préférence d'une solution plus universelle par rapport à une solution plus limitée. Veuillez considérer que, des années plus tard, des gens (comme moi) s'arrêteront pour chercher cette réponse et seront peut-être heureux d'en trouver une qui soit utile dans un cadre plus large que la question d'origine. Comme on dit dans le monde Open Source: "le choix est bon!"
Carl Smotricz
2
@technosaurus, FWIW [[ $string == *foo* ]]fonctionne également dans certaines versions sh conformes POSIX (par exemple /usr/xpg4/bin/shsur Solaris 10) et ksh (> = 88)
maxschlepzig
3
Busybox ash ne gère pas les astérisques dans [[... le cas-switch a fonctionné!
Ray Foss
233

stringContain variantes (compatibles ou indépendantes du boîtier)

Comme ces réponses Stack Overflow parlent principalement de Bash , j'ai posté une fonction Bash indépendante de la casse tout en bas de cet article ...

Quoi qu'il en soit, il y a mon

Réponse compatible

Comme il y a déjà beaucoup de réponses en utilisant des fonctionnalités spécifiques à Bash, il existe un moyen de travailler sous des shells plus pauvres, comme BusyBox :

[ -z "${string##*$reqsubstr*}" ]

En pratique, cela pourrait donner:

string='echo "My string"'
for reqsubstr in 'o "M' 'alt' 'str';do
  if [ -z "${string##*$reqsubstr*}" ] ;then
      echo "String '$string' contain substring: '$reqsubstr'."
    else
      echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
  done

Cela a été testé sous Bash, Dash , KornShell ( ksh) et ash (BusyBox), et le résultat est toujours:

String 'echo "My string"' contain substring: 'o "M'.
String 'echo "My string"' don't contain substring: 'alt'.
String 'echo "My string"' contain substring: 'str'.

En une seule fonction

Comme l'a demandé @EeroAaltonen, voici une version de la même démo, testée sous les mêmes shells:

myfunc() {
    reqsubstr="$1"
    shift
    string="$@"
    if [ -z "${string##*$reqsubstr*}" ] ;then
        echo "String '$string' contain substring: '$reqsubstr'.";
      else
        echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
}

Alors:

$ myfunc 'o "M' 'echo "My String"'
String 'echo "My String"' contain substring 'o "M'.

$ myfunc 'alt' 'echo "My String"'
String 'echo "My String"' don't contain substring 'alt'.

Remarque: vous devez échapper ou insérer des guillemets doubles et / ou des guillemets doubles:

$ myfunc 'o "M' echo "My String"
String 'echo My String' don't contain substring: 'o "M'.

$ myfunc 'o "M' echo \"My String\"
String 'echo "My String"' contain substring: 'o "M'.

Fonction simple

Cela a été testé sous BusyBox, Dash et, bien sûr, Bash:

stringContain() { [ -z "${2##*$1*}" ]; }

Alors maintenant:

$ if stringContain 'o "M3' 'echo "My String"';then echo yes;else echo no;fi
no
$ if stringContain 'o "M' 'echo "My String"';then echo yes;else echo no;fi
yes

... Ou si la chaîne soumise pouvait être vide, comme l'a souligné @Sjlver, la fonction deviendrait:

stringContain() { [ -z "${2##*$1*}" ] && [ -z "$1" -o -n "$2" ]; }

ou comme suggéré par le commentaire d'Adrian Günter , en évitant les -ochangements:

stringContain() { [ -z "${2##*$1*}" ] && { [ -z "$1" ] || [ -n "$2" ];};}

Fonction finale (simple):

Et inverser les tests pour les rendre potentiellement plus rapides:

stringContain() { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ];};}

Avec des chaînes vides:

$ if stringContain '' ''; then echo yes; else echo no; fi
yes
$ if stringContain 'o "M' ''; then echo yes; else echo no; fi
no

Indépendant du cas (Bash uniquement!)

Pour tester des chaînes sans se soucier de la casse, convertissez simplement chaque chaîne en minuscules:

stringContain() {
    local _lc=${2,,}
    [ -z "$1" ] || { [ -z "${_lc##*${1,,}*}" ] && [ -n "$2" ] ;} ;}

Vérifier:

stringContain 'o "M3' 'echo "my string"' && echo yes || echo no
no
stringContain 'o "My' 'echo "my string"' && echo yes || echo no
yes
if stringContain '' ''; then echo yes; else echo no; fi
yes
if stringContain 'o "M' ''; then echo yes; else echo no; fi
no
F. Hauri
la source
1
Ce serait encore mieux, si vous pouvez trouver un moyen de mettre cela en fonction.
Eero Aaltonen
2
@EeroAaltonen Comment trouvez-vous ma fonction (nouvelle ajoutée)?
F. Hauri
2
Je connais! trouver . -nom "*" | xargs grep "myfunc" 2> / dev / null
eggmatters
6
C'est merveilleux car il est tellement compatible. Un bogue, cependant: cela ne fonctionne pas si la chaîne de meule de foin est vide. La version correcte serait string_contains() { [ -z "${2##*$1*}" ] && [ -n "$2" -o -z "$1" ]; } une dernière pensée: la chaîne vide contient-elle la chaîne vide? La version au dessus des choses oui (à cause de la -o -z "$1"partie).
Sjlver
3
+1. Très bien! Pour moi, j'ai changé l'ordre stringContain () {[-z "$ {1 ## * $ 2 *}"] && [-z "$ 2" -o -n "$ 1"]; }; "Rechercher où" "Trouver quoi". Travaillez dans busybox. La réponse acceptée ci-dessus ne fonctionne pas dans la boîte occupée.
user3439968
149

Vous devez vous rappeler que les scripts shell sont moins un langage et plus une collection de commandes. Instinctivement, vous pensez que ce «langage» vous oblige à suivre un ifavec un [ou un [[. Ces deux sont juste des commandes qui renvoient un état de sortie indiquant le succès ou l'échec (comme toutes les autres commandes). Pour cette raison grep, j'utiliserais , et non la [commande.

Faites juste:

if grep -q foo <<<"$string"; then
    echo "It's there"
fi

Maintenant que vous envisagez de iftester l'état de sortie de la commande qui la suit (avec point-virgule), pourquoi ne pas reconsidérer la source de la chaîne que vous testez?

## Instead of this
filetype="$(file -b "$1")"
if grep -q "tar archive" <<<"$filetype"; then
#...

## Simply do this
if file -b "$1" | grep -q "tar archive"; then
#...

L' -qoption fait que grep ne produit rien, car nous voulons seulement le code retour. <<<fait que le shell développe le mot suivant et l'utilise comme entrée de la commande, une version à une ligne du <<document ici (je ne sais pas si c'est standard ou un bashisme).

Mark Baker
la source
7
on les appelle ici des cordes (3.6.7) je crois que c'est du
bashisme
10
on peut aussi utiliser Process Substitution if grep -q foo <(echo somefoothing); then
larsr
Notez que ce echon'est pas transférable, si vous passez une variable, utilisez printf '%s' "$stringplutôt.
nyuszika7h
5
Le coût de ce prix est très élevé: faire grep -q foo <<<"$mystring"implie une fourche et est bash et echo $mystring | grep -q fooimplie 2 fourchettes (un pour la conduite et la seconde pour la course /path/to/grep)
F. Hauri
1
@BrunoBronosky echosans indicateurs peut toujours avoir des problèmes de portabilité inattendus si la chaîne d'argument contient des séquences de barre oblique inverse. echo "nope\c"est prévu sur certaines plates-formes pour fonctionner comme echo -e "nope"sur d'autres. printf '%s' "nope"vsprintf '%s\n' 'nope\c'
tripleee
92

La réponse acceptée est la meilleure, mais comme il existe plusieurs façons de le faire, voici une autre solution:

if [ "$string" != "${string/foo/}" ]; then
    echo "It's there!"
fi

${var/search/replace}est $varavec la première instance de searchremplacée par replace, si elle est trouvée (elle ne change pas $var). Si vous essayez de remplacer foopar rien et que la chaîne a changé, alors, évidemment, a fooété trouvé.

éphémère
la source
5
solution éphémère ci-dessus:> `if [" $ string "! =" $ {string / foo /} "]; puis faites écho "C'est là!" fi` est utile lors de l'utilisation de la cendre de shell de BusyBox . La solution acceptée ne fonctionne pas avec BusyBox car certaines expressions régulières de bash ne sont pas implémentées.
TPoschel
1
l'inégalité de la différence. Pensée assez bizarre! Je l'adore
nitinr708
1
à moins que votre chaîne ne soit "foo"
venimus
J'ai écrit cette même solution moi-même (parce que mon interprète ne prendrait pas les meilleures réponses) puis je suis allé en chercher une meilleure, mais j'ai trouvé ça!
BuvinJ
56

Il existe donc de nombreuses solutions utiles à la question - mais laquelle est la plus rapide / utilise le moins de ressources?

Tests répétés en utilisant ce cadre:

/usr/bin/time bash -c 'a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done'

Remplacement de TEST à chaque fois:

[[ $b =~ $a ]]           2.92 user 0.06 system 0:02.99 elapsed 99% CPU

[ "${b/$a//}" = "$b" ]   3.16 user 0.07 system 0:03.25 elapsed 99% CPU

[[ $b == *$a* ]]         1.85 user 0.04 system 0:01.90 elapsed 99% CPU

case $b in *$a):;;esac   1.80 user 0.02 system 0:01.83 elapsed 99% CPU

doContain $a $b          4.27 user 0.11 system 0:04.41 elapsed 99%CPU

(doContain était dans la réponse de F. Houri)

Et pour les rires:

echo $b|grep -q $a       12.68 user 30.86 system 3:42.40 elapsed 19% CPU !ouch!

Ainsi, l'option de substitution simple l'emporte de manière prévisible que ce soit dans un test étendu ou dans un cas. L'affaire est portable.

Prévoir 100 000 greps est douloureusement prévisible! L'ancienne règle concernant l'utilisation d'utilitaires externes sans besoin est vraie.

Paul Hedderly
la source
4
Référence soignée. M'a convaincu de l'utiliser [[ $b == *$a* ]].
Mad Physicist
2
Si je lis bien, casegagne avec la plus petite consommation de temps globale. Il vous manque cependant un astérisque $b in *$a. J'obtiens des résultats légèrement plus rapides pour [[ $b == *$a* ]]que pour caseavec le bug corrigé, mais cela peut dépendre aussi d'autres facteurs, bien sûr.
tripleee
1
ideone.com/5roEVt a mon expérience avec quelques bugs supplémentaires corrigés et teste un scénario différent (où la chaîne n'est en fait pas présente dans la chaîne plus longue). Les résultats sont largement similaires; [[ $b == *$a* ]]est rapide et casepresque aussi rapide (et agréablement compatible POSIX).
tripleee
28

Cela fonctionne également:

if printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  printf "Found needle in haystack"
fi

Et le test négatif est:

if ! printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  echo "Did not find needle in haystack"
fi

Je suppose que ce style est un peu plus classique - moins dépendant des fonctionnalités du shell Bash.

L' --argument est de la pure paranoïa POSIX, utilisé pour protéger contre les chaînes d'entrée similaires aux options, telles que --abcou -a.

Remarque: Dans une boucle étroite, ce code sera beaucoup plus lent que l'utilisation des fonctionnalités internes du shell Bash, car un (ou deux) processus distincts seront créés et connectés via des tuyaux.

kevinarpe
la source
5
... mais l'OP ne dit pas quelle version de bash; par exemple, les bash plus anciens (comme Solaris l'a souvent) peuvent ne pas inclure ces nouvelles fonctionnalités bash. (J'ai rencontré ce problème exact (correspondance de modèle bash non implémentée) sur Solaris avec bash 2.0)
Michael
2
echon'est pas transférable, vous devriez utiliser à la printf '%s' "$haystackplace.
nyuszika7h
2
Non, évitez echotout simplement autre chose que du texte littéral sans échappements qui ne commence pas par un -. Cela peut fonctionner pour vous, mais ce n'est pas portable. Même bash echose comportera différemment selon que l' xpg_echooption est définie ou non. PS: j'ai oublié de fermer la double citation dans mon commentaire précédent.
nyuszika7h
1
@kevinarpe Je ne suis pas sûr, --n'est pas répertorié dans la spécification POSIX pourprintf , mais vous devez printf '%s' "$anything"quand même l' utiliser pour éviter les problèmes s'il $anythingcontient un %caractère.
nyuszika7h
1
@kevinarpe Sur la base de cela, c'est probablement le cas.
nyuszika7h
21

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 de 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

Exemple 8, caractère générique .ext (insensible à la casse):

     if echo "$a" | egrep -iq "\.(mp[3-4]|txt|css|jpg|png)" ; then

Prendre plaisir.

Mike Q
la source
17

Que dis-tu de ça:

text="   <tag>bmnmn</tag>  "
if [[ "$text" =~ "<tag>" ]]; then
   echo "matched"
else
   echo "not matched"
fi
bn.
la source
2
= ~ est pour la correspondance d'expression rationnelle, donc trop puissant pour le but de l'OP.
16

Comme Paul l'a mentionné dans sa comparaison de performances:

if echo "abcdefg" | grep -q "bcdef"; then
    echo "String contains is true."
else
    echo "String contains is not true."
fi

Ceci est conforme à POSIX comme le 'case "$ string" dans' la réponse fournie par Marcus , mais il est légèrement plus facile à lire que la réponse à la déclaration de cas. Notez également que cela sera beaucoup plus lent que l'utilisation d'une déclaration de cas. Comme l'a souligné Paul, ne l'utilisez pas en boucle.

Samuel
la source
11

Cette réponse Stack Overflow était la seule à intercepter les caractères d'espace et de tiret:

# For null cmd arguments checking   
to_check=' -t'
space_n_dash_chars=' -'
[[ $to_check == *"$space_n_dash_chars"* ]] && echo found
Yordan Georgiev
la source
C'est une réponse à cette même question.
Peter Mortensen
9
[[ $string == *foo* ]] && echo "It's there" || echo "Couldn't find"
Jahid
la source
J'ajouterai que l' echo "Couldn't findinstruction à la fin est une bonne astuce pour retourner 0 états de sortie pour ces commandes correspondantes.
nicodjimenez
@nicodjimenez vous ne pouvez plus cibler le statut de sortie avec cette solution. Le statut de sortie est englouti par les messages d'état ...
Jahid
1
C'est exactement ce que je voulais dire ... Si vous n'en avez pas, || echo "Couldn't find"vous retournerez un état de sortie d'erreur s'il n'y a pas de correspondance, ce que vous ne voudrez peut-être pas si vous exécutez un pipeline CI, par exemple où vous voulez que toutes les commandes retournent statuts de sortie sans erreur
nicodjimenez
9

L'un est:

[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' ||  echo 'no'
chemila
la source
2
exprest l'un de ces utilitaires de couteau de l'armée suisse qui peut généralement faire tout ce que vous devez faire, une fois que vous avez compris comment le faire, mais une fois implémenté, vous ne vous souvenez plus pourquoi ni comment il fait ce qu'il fait, donc vous ne le touchez plus jamais, et espérez qu'il ne cesse de faire ce qu'il fait.
michael
@AloisMahdal Je n'ai jamais voté contre, je postule simplement pourquoi des votes négatifs ont été accordés. Un commentaire d'avertissement. J'utilise expr, à de rares occasions, lorsque la portabilité empêche d'utiliser bash (par exemple, un comportement incohérent dans les anciennes versions), tr (incohérent partout) ou sed (parfois trop lent). Mais par expérience personnelle, chaque fois que exprje relis ces -ismes, je dois retourner à la page de manuel. Donc, je voudrais simplement faire remarquer que chaque utilisation de exprêtre commentée ...
michael
1
Il fut un temps où tout ce que vous aviez était le shell Bourne d'origine. Il manquait certaines fonctionnalités couramment requises, donc des outils comme expret testont été mis en œuvre pour les exécuter. De nos jours, il existe généralement de meilleurs outils, dont beaucoup sont intégrés dans n'importe quelle coque moderne. Je suppose qu'il testest toujours là, mais personne ne semble manquer expr.
tripleee
6

J'aime sed.

substr="foo"
nonsub="$(echo "$string" | sed "s/$substr//")"
hassub=0 ; [ "$string" != "$nonsub" ] && hassub=1

Édition, logique:

  • Utilisez sed pour supprimer l'instance de sous-chaîne de la chaîne

  • Si la nouvelle chaîne diffère de l'ancienne chaîne, la sous-chaîne existe

balade
la source
2
Veuillez ajouter quelques explications. Il est plus important d'impartir la logique sous-jacente que de simplement donner le code, car cela aide l'OP et d'autres lecteurs à résoudre eux
Zulan
5

grep -q est utile à cet effet.

La même chose en utilisant awk:

string="unix-bash 2389"
character="@"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

Production:

Pas trouvé

string="unix-bash 2389"
character="-"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

Production:

A trouvé

Source d'origine: http://unstableme.blogspot.com/2008/06/bash-search-letter-in-string-awk.html

nyuszika7h
la source
3
echon'est pas transférable, vous devriez utiliser à la printf '%s' "$string"place. J'édite la réponse car l'utilisateur ne semble plus exister.
nyuszika7h
De quelle manière n'est echopas portable, @ nyuszika7h?
starbeamrainbowlabs
5

J'ai constaté que j'avais besoin de cette fonctionnalité assez fréquemment, donc j'utilise une fonction shell faite maison dans mon .bashrccomme celle-ci qui me permet de la réutiliser aussi souvent que nécessaire, avec un nom facile à retenir:

function stringinstring()
{
    case "$2" in
       *"$1"*)
          return 0
       ;;
    esac
    return 1
}

Pour tester si $string1(par exemple, abc ) est contenu dans $string2(par exemple, 123abcABC ), il me suffit d'exécuter stringinstring "$string1" "$string2"et de vérifier la valeur de retour, par exemple

stringinstring "$str1" "$str2"  &&  echo YES  ||  echo NO
Kurt Pfeifle
la source
[["$ str" == $ substr ]] && écho OUI || echo NO
elyase
Je suis sûr que le xhack n'est requis que pour les très vieux obus.
nyuszika7h
5

Mon .bash_profile fichier et comment j'ai utilisé grep:

Si la variable d'environnement PATH inclut mes deux binrépertoires, ne les ajoutez pas,

# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

U=~/.local.bin:~/bin

if ! echo "$PATH" | grep -q "home"; then
    export PATH=$PATH:${U}
fi
Leslie Satenstein
la source
1
Est-ce une réponse?
codeforester
upvote. pourquoi prendre la peine d'apprendre comment Bash le fait lorsque grep, beaucoup plus puissant, sera plus que probablement disponible. étendre également un peu plus loin en correspondance avec une liste de motifs: grep -q -E 'pattern1|...|patternN'.
Mohamed Bana
4

Extension de la question répondue ici Comment savoir si une chaîne contient une autre chaîne dans POSIX sh? :

Cette solution fonctionne avec des caractères spéciaux:

# contains(string, substring)
#
# Returns 0 if the specified string contains the specified substring,
# otherwise returns 1.
contains() {
    string="$1"
    substring="$2"

    if echo "$string" | $(type -p ggrep grep | head -1) -F -- "$substring" >/dev/null; then
        return 0    # $substring is in $string
    else
        return 1    # $substring is not in $string
    fi
}

contains "abcd" "e" || echo "abcd does not contain e"
contains "abcd" "ab" && echo "abcd contains ab"
contains "abcd" "bc" && echo "abcd contains bc"
contains "abcd" "cd" && echo "abcd contains cd"
contains "abcd" "abcd" && echo "abcd contains abcd"
contains "" "" && echo "empty string contains empty string"
contains "a" "" && echo "a contains empty string"
contains "" "a" || echo "empty string does not contain a"
contains "abcd efgh" "cd ef" && echo "abcd efgh contains cd ef"
contains "abcd efgh" " " && echo "abcd efgh contains a space"

contains "abcd [efg] hij" "[efg]" && echo "abcd [efg] hij contains [efg]"
contains "abcd [efg] hij" "[effg]" || echo "abcd [efg] hij does not contain [effg]"

contains "abcd *efg* hij" "*efg*" && echo "abcd *efg* hij contains *efg*"
contains "abcd *efg* hij" "d *efg* h" && echo "abcd *efg* hij contains d *efg* h"
contains "abcd *efg* hij" "*effg*" || echo "abcd *efg* hij does not contain *effg*"
Alex Skrypnyk
la source
Le test contains "-n" "n"ne fonctionne pas ici, car echo -nva avaler le -nen option! Une solution populaire pour cela est d'utiliser à la printf "%s\n" "$string"place.
joeytwiddle
parfaitement, la chaîne résolue contenait des espaces
dengApro
4

Étant donné que la question POSIX / BusyBox est fermée sans fournir la bonne réponse (IMHO), je posterai une réponse ici.

La réponse la plus courte possible est:

[ ${_string_##*$_substring_*} ] || echo Substring found!

ou

[ "${_string_##*$_substring_*}" ] || echo 'Substring found!'

Notez que le double hachage est obligatoire avec certains shells (ash). Ci-dessus évaluera[ stringvalue ]quand la sous-chaîne n'est pas trouvée. Il ne renvoie aucune erreur. Lorsque la sous-chaîne est trouvée, le résultat est vide et il est évalué[ ]. Cela générera le code d'erreur 1 car la chaîne est complètement remplacée (en raison de*).

La syntaxe la plus courte et la plus courante:

[ -z "${_string_##*$_substring_*}" ] && echo 'Substring found!'

ou

[ -n "${_string_##*$_substring_*}" ] || echo 'Substring found!'

Un autre:

[ "${_string_##$_substring_}" != "$_string_" ] && echo 'Substring found!'

ou

[ "${_string_##$_substring_}" = "$_string_" ] || echo 'Substring found!'

Notez le seul signe égal!

FifthAxiom
la source
3

Correspondance exacte des mots:

string='My long string'
exactSearch='long'

if grep -E -q "\b${exactSearch}\b" <<<${string} >/dev/null 2>&1
  then
    echo "It's there"
  fi
Eduardo Cuomo
la source
3

Essayez oobash.

Il s'agit d'une bibliothèque de chaînes de style OO pour Bash 4. Elle prend en charge les trémas allemands. Il est écrit en Bash.

De nombreuses fonctions sont disponibles: -base64Decode, -base64Encode, -capitalize, -center, -charAt, -concat, -contains, -count, -endsWith, -equals, -equalsIgnoreCase, -reverse, -hashCode, -indexOf, -isAlnum, -isAlpha, -isAscii, -isDigit, -isEmpty, -isHexDigit, -isLowerCase, -isSpace, -isPrintable, -isUpperCase, -isVisible, -lastIndexOf, -length, -matches, -replaceAll, -replaceFirst, -startsWith, -substring, -swapCase, -toLowerCase, -toString, -toUpperCase, -trimet-zfill .

Regardez l' exemple contient :

[Desktop]$ String a testXccc
[Desktop]$ a.contains tX
true
[Desktop]$ a.contains XtX
false

oobash est disponible sur Sourceforge.net .

andreas
la source
2

J'utilise cette fonction (une dépendance non incluse mais évidente). Il passe les tests ci-dessous. Si la fonction renvoie une valeur> 0, la chaîne a été trouvée. Vous pouvez tout aussi bien retourner 1 ou 0 à la place.

function str_instr {
   # Return position of ```str``` within ```string```.
   # >>> str_instr "str" "string"
   # str: String to search for.
   # string: String to search.
   typeset str string x
   # Behavior here is not the same in bash vs ksh unless we escape special characters.
   str="$(str_escape_special_characters "${1}")"
   string="${2}"
   x="${string%%$str*}"
   if [[ "${x}" != "${string}" ]]; then
      echo "${#x} + 1" | bc -l
   else
      echo 0
   fi
}

function test_str_instr {
   str_instr "(" "'foo@host (dev,web)'" | assert_eq 11
   str_instr ")" "'foo@host (dev,web)'" | assert_eq 19
   str_instr "[" "'foo@host [dev,web]'" | assert_eq 11
   str_instr "]" "'foo@host [dev,web]'" | assert_eq 19
   str_instr "a" "abc" | assert_eq 1
   str_instr "z" "abc" | assert_eq 0
   str_instr "Eggs" "Green Eggs And Ham" | assert_eq 7
   str_instr "a" "" | assert_eq 0
   str_instr "" "" | assert_eq 0
   str_instr " " "Green Eggs" | assert_eq 6
   str_instr " " " Green "  | assert_eq 1
}
Ethan Post
la source
Le suspense! Quelle est la dépendance?
Peter Mortensen
La fonction "str_escape_special_characters". Il se trouve dans le fichier GitHub arcshell_str.sh. arcshell.io vous y amènera.
Ethan Post
0
msg="message"

function check {
    echo $msg | egrep [abc] 1> /dev/null

    if [ $? -ne 1 ];
    then 
        echo "found" 
    else 
        echo "not found" 
    fi
}

check

Cela trouvera toute occurrence de a ou b ou c

BobMonk
la source
0

L' exemple générique de meule de foin à aiguilles suit avec des variables

#!/bin/bash

needle="a_needle"
haystack="a_needle another_needle a_third_needle"
if [[ $haystack == *"$needle"* ]]; then
    echo "needle found"
else
    echo "needle NOT found"
fi
Pipo
la source