Testen Sie, ob eine Zeichenfolge eine Teilzeichenfolge enthält

40

Ich habe den Code

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

Ich teste ob file"gen" enthalten ist. Die Ausgabe ist "False". Nett!

Das Problem ist, wenn ich "gen" durch eine Variable ersetze testseq:

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

Jetzt ist die Ausgabe "True". Wie könnte es sein? Wie kann das Problem behoben werden?

Viesturs
quelle

Antworten:

25

Sie müssen die $testseqVariable auf eine der folgenden Arten interpolieren :

  • $file == *_"$testseq"_*(hier $testseqals feste Zeichenfolge betrachtet)

  • $file == *_${testseq}_*(hier $testseqals Muster angesehen).

Oder der _Name unmittelbar nach der Variablen wird als Teil des Variablennamens verwendet (es ist ein gültiges Zeichen in einem Variablennamen).

RomanPerekhrest
quelle
Richtige Antwort, da dies für OP gilt, aber nicht portabel ist. (Dies ist keine Kritik an der angegebenen Antwort, sondern nur eine Warnung an die Leser). ;-)
Cbhihe
28

Verwenden Sie den =~Operator, um Vergleiche mit regulären Ausdrücken durchzuführen:

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

Auf diese Weise wird es vergleichen , wenn $filehat $testseqseinen Inhalt auf.

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

Wenn ich mich ändere testseq="Const":

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

Aber seien Sie vorsichtig mit dem, was Sie füttern $testseq. Wenn die Zeichenfolge darauf irgendwie einen regulären Ausdruck darstellt (wie [0-9]zum Beispiel), besteht eine größere Chance, eine "Übereinstimmung" auszulösen.

Referenz :


quelle
20
file="JetConst_reco_allconst_4j2t.png"
testseq="gen"

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

Die Verwendung case ... esacist eine der einfachsten Methoden, um einen Mustervergleich auf tragbare Weise durchzuführen. Es funktioniert wie eine „switch“ Anweisung in anderen Sprachen ( bash, zsh, und ksh93können Sie auch tun , Durchfall in verschiedener inkompatible Weise). Die verwendeten Muster sind die Globbing-Muster für Standarddateinamen.

Das Problem, das Sie haben, ist auf die Tatsache zurückzuführen, dass _es sich bei einem Variablennamen um ein gültiges Zeichen handelt. Die Shell wird daher *_$testseq_*als " *_gefolgt von dem Wert der Variablen $testseq_und einem *" angezeigt . Die Variable $testseq_ist undefiniert, daher wird sie zu einer leeren Zeichenfolge erweitert, und am Ende erhalten Sie eine *_*, die offensichtlich mit dem von $fileIhnen angegebenen Wert übereinstimmt. Sie können erwarten, Truesolange der Dateiname $filemindestens einen Unterstrich enthält.

Um richtig den Namen der Variablen, die Verwendung zu begrenzen "..."um die Erweiterung: *_"$testseq"_*. Dies würde den Wert der Variablen als Zeichenfolge verwenden. Möchten Sie den Wert der Variablen als Muster verwenden , verwenden Sie *_${testseq}_*stattdessen.

Eine weitere schnelle Lösung besteht darin, die Unterstriche in den Wert von einzuschließen $testseq:

testseq="_gen_"

und dann einfach *"$testseq"*als Muster verwenden (für einen Zeichenkettenvergleich).

Kusalananda
quelle
Die Shell sucht also nach einer Variablen $ testseq_ und findet sie nicht und ersetzt sie durch eine leere Zeichenfolge.
Viesturs
@Viesturs Das ist das Herzstück des Problems, ja.
Kusalananda
1
Für einen Teilkette suchen soll es sein , *"$testseq"*für casewie für [[...]](außer zsh es sei denn , Sie aktivieren globsubst)
Stéphane Chazelas