Comment déterminer si une chaîne est une sous-chaîne d'une autre dans bash?

49

Je veux voir si une chaîne est à l'intérieur d' une partie d'une autre chaîne.
par exemple:

'ab' in 'abc' -> true
'ab' in 'bcd' -> false

Comment puis-je faire cela dans les conditions d'un script bash?

Lucio
la source

Réponses:

27

Vous pouvez utiliser le formulaire ${VAR/subs}VARcontient la chaîne la plus grande et subsconstitue la sous-chaîne que vous essayez de trouver:

my_string=abc
substring=ab
if [ "${my_string/$substring}" = "$my_string" ] ; then
  echo "${substring} is not in ${my_string}"
else
  echo "${substring} was found in ${my_string}"
fi

Cela fonctionne car ${VAR/subs}est égal à $VARmais avec la première occurrence de la chaîne subssupprimée, en particulier si $VARne contient pas le mot, subsil ne sera pas modifié.

Edwin
la source
Je pense que vous devriez changer la séquence des echodéclarations. Parce que je reçoisab is not in abc
Lucio
Tu as raison! : P
edwin
Mmm .. Non, le script est faux. Comme ça, je reçois ab was found in abc, mais si j’utilise, substring=zje reçoisz was found in abc
Lucio
1
Maintenant je comprends ab is not in abc. Mais z was found in abc. C'est drôle: D
Lucio
1
Duh! Les échos étaient juste au début de ceci! XD
Edwin
47

[[ "bcd" =~ "ab" ]]
[[ "abc" =~ "ab" ]]

les crochets sont pour le test, et comme il s’agit de crochets doubles, il peut donc contenir des tests supplémentaires comme =~.

Donc, vous pouvez utiliser ce formulaire quelque chose comme

var1="ab"
var2="bcd"
if [[ "$var2" =~ "$var1" ]]; then
    echo "pass"
else
    echo "fail"
fi

Edit: corrigé "= ~", s'était retourné.

sage
la source
1
Je reçois failavec ces paramètres:var2="abcd"
Lucio
3
@ Lucio Le bon est [[ $string =~ $substring ]]. J'ai mis à jour la réponse.
Eric Carvalho
12

Utilisation de modèles de nom de fichier bash ( modèles "glob")

substr=ab
[[ abc == *"$substr"* ]] && echo yes || echo no    # yes
[[ bcd == *"$substr"* ]] && echo yes || echo no    # no
Glenn Jackman
la source
if [["$ JAVA_OPTS"! = "-XX: + UseCompressedOops" ]]; puis exportez JAVA_OPTS = "$ JAVA_OPTS -XX: + UseCompressedOops"; fi
Mike Slinn
10

Les deux approches suivantes fonctionneront sur n’importe quel environnement compatible POSIX, pas seulement en bash:

substr=ab
for s in abc bcd; do
    if case ${s} in *"${substr}"*) true;; *) false;; esac; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done
substr=ab
for s in abc bcd; do
    if printf %s\\n "${s}" | grep -qF "${substr}"; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done

Les deux sorties ci-dessus:

'abc' contains 'ab'
'bcd' does not contain 'ab'

Le premier a l'avantage de ne pas engendrer un grepprocessus séparé .

Notez que j'utilise printf %s\\n "${foo}"au lieu de echo "${foo}"parce que echopourrait être mutilé ${foo}s'il contient des barres obliques inverses.

Richard Hansen
la source
La première version fonctionne parfaitement pour rechercher une sous-chaîne de nom de moniteur dans la liste des xrandrnoms de moniteur stockés dans une variable. +1 et bienvenue dans le club représentant 1K :)
WinEunuuchs2Unix
6

déclaration de cas

Ceci est la solution la plus portable, fonctionnera même sur les vieux obus Bourne et Korn

#!/bin/bash
case "abcd" in
    *$1*) echo "It's a substring" ;;
    *) echo "Not a substring" ;;
esac

Échantillon échantillon:

$ ./case_substr.sh "ab"                                                                                           
It's a substring
$ ./case_substr.sh "whatever"                                                                                     
Not a substring

Notez que vous ne devez pas utiliser spécifiquement, echovous pouvez utiliser exit 1et exit 0signifier le succès ou l'échec.

Ce que nous pourrions aussi faire, c’est créer une fonction (qui peut être utilisée dans de gros scripts si nécessaire) avec des valeurs de retour spécifiques (0 sur correspondance, 1 sur absence de correspondance):

$ ./substring_function.sh                                  
ab is substring

$ cat substring_function.sh                                
#!/bin/sh

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

main(){
   if is_substring "ab" "abcdefg"
   then
       echo "ab is substring"
   fi
}

main $@

grep

$ grep -q 'ab' <<< "abcd" && echo "it's a substring" || echo "not a substring"                                    
it's a substring

Cette approche particulière est utile avec les déclarations if-else dans bash. Aussi principalement portable

AWK

$ awk '$0~/ab/{print "it is a substring"}' <<< "abcd"                                                             
it is a substring

Python

$ python -c 'import sys;sys.stdout.write("it is a substring") if "ab" in sys.stdin.read() else exit(1)' <<< "abcd"
it is a substring

Rubis

$ ruby -e ' puts "is substring" if  ARGV[1].include? ARGV[0]'  "ab" "abcdef"                                             
is substring
Sergiy Kolodyazhnyy
la source
+1 pour aller au-delà des autres. J'ai remarqué ici et sur d'autres sites d'échange de pile, aucune réponse ne renvoie le décalage de la sous-chaîne dans la chaîne. Quelle est la mission de ce soir :)
WinEunuuchs2Unix
@ WinEunuuchs2Unix Vous allez le faire en bash?
Sergiy Kolodyazhnyy
Oui et non. Je fais un projet Frankenstein où python récupère toutes les métadonnées des messages de gmail.com et que bash analyse et présente une liste d'interface graphique avec un zoom avant. J'ai trouvé la réponse ici: stackoverflow.com/questions/5031764/…
WinEunuuchs2Unix
@ WinEunuuchs2Unix OK. Ça semble intéressant. Personnellement, je préférerais tout analyser en Python. Il offre bien plus de possibilités de traitement de texte que bash seul.
Sergiy Kolodyazhnyy
Je connais vos préférences depuis environ deux ans et je les respecte. Mais j'apprends juste le python et le fait de travailler avec yad me semble compliqué. Sans parler de tous les traitements de matrice avec lesquels je suis déjà à l'aise dans bash. Mais au moins, j’ai écrit mon premier script python pour extraire tout du contenu de gmail.com de Google dans un fichier plat Linux, non? :)
WinEunuuchs2Unix
5

Attention au [[et ":

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

[ $a == z* ]     # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).

Donc, comme @glenn_jackman l'a dit, mais sachez que si vous mettez tout le deuxième terme entre guillemets, le test passera à la correspondance littérale .

Source: http://tldp.org/LDP/abs/html/comparison-ops.html

Campa
la source
4

Semblable à la réponse d’edwin, mais avec une portabilité améliorée pour posix & ksh, et un toucher moins bruyant que celui de Richard:

substring=ab

string=abc
if [ "$string" != "${string%$substring*}" ]; then
    echo "$substring IS in $string"
else
    echo "$substring is NOT in $string"
fi

string=bcd
if [ "$string" != "${string%$substring*}" ]; then
    echo "$string contains $substring"
else
    echo "$string does NOT contain $substring"
fi

Sortie:

abc contains ab
bcd does NOT contain ab
laubster
la source