Teste si une chaîne contient une sous-chaîne

42

J'ai le code

file="JetConst_reco_allconst_4j2t.png"
if [[ $file == *_gen_* ]];
then
    echo "True"
else
    echo "False"
fi

Je teste si filecontient "gen". La sortie est "False". Agréable!

Le problème est quand je substitue "gen" avec une variable testseq:

file="JetConst_reco_allconst_4j2t.png"
testseq="gen"
if [[ $file == *_$testseq_* ]];
then
    echo "True"
else
    echo "False"
fi

Maintenant, le résultat est "True". Comment est-ce possible? Comment resoudre le probleme?

Viesturs
la source

Réponses:

26

Vous devez interpoler la $testseqvariable de l’une des manières suivantes:

  • $file == *_"$testseq"_*(ici $testseqconsidéré comme une chaîne fixe)

  • $file == *_${testseq}_*(ici $testseqconsidéré comme un motif).

Ou _tout de suite après le nom de la variable sera considéré comme faisant partie du nom de la variable (c'est un caractère valide dans un nom de variable).

RomanPerekhrest
la source
La réponse correcte s’applique à OP, mais n’est pas portable. (Ce n'est pas une critique de la réponse fournie, c'est juste un avertissement aux lecteurs). ;-)
Cbhihe
29

Utilisez l' =~opérateur pour créer des combinaisons d'expressions régulières:

#!/bin/bash
file="JetConst_reco_allconst_4j2t.png"
testseq="gen"
if [[ $file =~ $testseq ]];
then
    echo "True"
else
    echo "False"
fi

De cette façon, il va comparer si $filea $testseqsur son contenu.

user@host:~$ ./string.sh
False

Si je change testseq="Const":

user@host:~$ ./string.sh
True

Mais soyez prudent avec ce que vous nourrissez $testseq. Si la chaîne qui y figure représente une expression rationnelle (comme [0-9]par exemple), il y a plus de chance de déclencher une "correspondance".

Référence :


la source
20
file="JetConst_reco_allconst_4j2t.png"
testseq="gen"

case "$file" in
    *_"$testseq"_*) echo 'True'  ;;
    *)              echo 'False'
esac

L’utilisation case ... esacest l’un des moyens les plus simples d’effectuer une correspondance de motif de manière portable. Il fonctionne comme un « switch » dans d' autres langues ( bash, zshet ksh93vous permet également de faire fall-through de diverses manières incompatibles). Les modèles utilisés sont les modèles globing de nom de fichier standard.

Le problème que vous rencontrez est dû au fait qu'il _s'agit d'un caractère valide dans un nom de variable. Le shell va donc voir *_$testseq_*" *_suivi de la valeur de la variable $testseq_et d'un *". La variable $testseq_est indéfinie, elle sera donc étendue à une chaîne vide et vous obtiendrez avec *_*, ce qui correspond évidemment à la $filevaleur que vous avez. Vous pouvez vous attendre à obtenir Trueaussi longtemps que le nom du fichier $filecontient au moins un trait de soulignement.

Pour délimiter correctement le nom de la variable, l' utilisation "..."autour de l'extension: *_"$testseq"_*. Cela utiliserait la valeur de la variable en tant que chaîne. Souhaitez-vous utiliser la valeur de la variable en tant que modèle , utilisez *_${testseq}_*plutôt.

Une autre solution consiste à inclure les traits de soulignement dans la valeur de $testseq:

testseq="_gen_"

et ensuite juste utiliser *"$testseq"*comme motif (pour une comparaison de chaîne).

Kusalananda
la source
Donc, le shell cherchera une variable $ testseq_ et ne la trouvera pas et la substituera avec une chaîne vide.
Viesturs
@Viesturs C'est le coeur du problème, oui.
Kusalananda
1
La recherche d'un sous - chaîne , il devrait être *"$testseq"*pour casecomme pour [[...]](sauf pour zsh moins que vous ne globsubst)
Stéphane Chazelas
Plus simple que [ "${str##*substr*}" ] || echo True?
Isaac il y a
@Isaac En termes de lecture et de compréhension de ce qui se passe, oui. Il est également facile d’étendre un test avec plus de tests sans obtenir des spaghettis «si-alors-alors-alors-alors-elif». Bien que tester une seule chaîne, la façon dont vous montrez (qu'une chaîne disparaisse dans une substitution) est plus courte.
Kusalananda
1

Pour le moyen portable de tester si une chaîne contient une sous-chaîne, utilisez:

file="JetConst_reco_allconst_4j2t.png";       testseq="gen"

[ "${file##*$testseq*}" ] || echo True Substring is present

Ou "${file##*"$testseq"*}"pour éviter d'interpréter les caractères globaux dans testseq.

Isaac
la source