web-dev-qa-db-fra.com

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

J'ai le code

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

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

Le problème est quand je remplace "gen" par une variable testseq:

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

Maintenant, la sortie est "True". Comment est-ce possible? Comment resoudre le probleme?

43
Viesturs

Vous devez interpoler le $testseq variable avec l'une des méthodes suivantes:

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

  • $file == *_${testseq}_* (ici $testseq considéré comme un modèle).

Ou la _ immédiatement 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).

28
RomanPerekhrest

Utilisez le =~ opérateur pour faire des comparaisons 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 comparera si $file a $testseq sur son contenu.

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

Si je change testseq="Const":

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

Mais attention à ce que vous nourrissez $testseq avec. Si la chaîne sur elle représente en quelque sorte une expression régulière (comme [0-9] par exemple), il y a plus de chances de déclencher une "correspondance".

Référence :

32
user34720
file="JetConst_reco_allconst_4j2t.png"
testseq="gen"

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

En utilisant case ... esac est l'un des moyens les plus simples d'effectuer une correspondance de modèle de manière portable. Il fonctionne comme une instruction "switch" dans d'autres langues (bash, zsh et ksh93 vous permet également de faire fall-through de différentes manières incompatibles). Les modèles utilisés sont les modèles de remplacement de nom de fichier standard.

Le problème que vous rencontrez est dû au fait que _ est un caractère valide dans un nom de variable. Le Shell verra ainsi *_$testseq_* comme "*_ suivi de la valeur de la variable $testseq_ Et un * ". La variable $testseq_ n'est pas défini, il sera donc développé en une chaîne vide et vous vous retrouverez avec *_*, qui correspond évidemment au $file valeur que vous avez. Vous pouvez vous attendre à obtenir True tant que le nom de fichier dans $file contient au moins un trait de soulignement.

Pour délimiter correctement le nom de la variable, utilisez "..." autour de l'expansion: *_"$testseq"_*. Cela utiliserait la valeur de la variable comme chaîne. Souhaitez-vous utiliser la valeur de la variable comme modèle, utilisez *_${testseq}_* au lieu.

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

testseq="_gen_"

puis utilisez simplement *"$testseq"* comme modèle (pour une comparaison de chaînes).

21
Kusalananda

Pour la méthode portable pour 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.

2
Isaac