Question String contient une sous-chaîne dans Bash


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. Est-ce que j'utilise l'écho et grep?

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

Cela a l'air un peu maladroit.


1742
2017-10-23 12:37


origine


Réponses:


Vous pouvez utiliser La réponse de Marcus (* caractères génériques) en dehors d'une déclaration de cas, aussi, 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, et le * les caractères génériques devraient être à l'extérieur.


2487
2017-10-23 12:55



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

string='My string';

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

386
2017-10-23 20:10



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

239
2017-10-23 12:47



Réponse compatible

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

[ -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 , ,  et  (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 demandé par @EeroAaltonen voici une version de la même démo, testé sous les mêmes coquilles:

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'.

Remarquer: vous devez vous échapper ou doublez les guillemets et / ou les guillemets:

$ 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 ,  et, bien sûr :

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

C'est tout les gens!

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 indiqué par @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 -o commutateur:

stringContain() { [ -z "${2##*$1*}" ] && { [ -z "$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

135
2017-12-08 23:06



Vous devriez vous rappeler que les scripts shell sont moins un langage et plus d'une collection de commandes. Instinctivement, vous pensez que cette "langue" vous oblige à suivre un if avec un [ ou un [[. Les deux sont juste des commandes qui retournent un état de sortie indiquant le succès ou l'échec (comme toutes les autres commandes). Pour cette raison, j'utiliserais grepet pas le [ commander.

Faites juste:

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

Maintenant que vous pensez à if en testant le statut de sortie de la commande qui le suit (complété par un 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
#...

le -q L'option fait que grep ne produit rien, car nous voulons seulement le code de retour. <<< fait le shell développer le mot suivant et l'utiliser comme entrée à la commande, une version d'une ligne de la << ici document (je ne suis pas sûr que ce soit standard ou un bashisme).


110
2017-10-27 14:57



La réponse acceptée est la meilleure, mais puisqu'il y a plus d'une façon de le faire, voici une autre solution:

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

${var/search/replace} est $var avec la première instance de search remplacé par replace, si on le trouve (ça ne change pas $var). Si vous essayez de remplacer foo par rien, et la chaîne a changé, alors évidemment foo a été trouvé.


73
2017-10-23 14:35



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

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

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

Remplacer TEST à chaque fois:

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

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

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

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

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

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

Et pour les fous rires:

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

Ainsi, l'option de substitution simple gagne, que ce soit dans un test étendu ou un cas. L'affaire est portable.

Piping à 100000 greps est prévisible douloureux! L'ancienne règle sur l'utilisation des utilitaires externes sans nécessité est vraie.


31
2017-08-27 19:42



Cela fonctionne aussi:

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 caractéristiques du shell Bash.

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

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


22
2017-12-01 15:41