Teste se uma sequência contém uma substring

40

Eu tenho o código

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

Eu testei se filecontém "gen". A saída é "False". Agradável!

O problema é quando substituo "gen" por uma variável testseq:

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

Agora a saída é "True". Como isso poderia ser? Como resolver o problema?

Viesturs
fonte
Possível duplicata do teste

Respostas:

25

Você precisa interpolar a $testseqvariável de uma das seguintes maneiras:

  • $file == *_"$testseq"_* (Aqui $testseq considerado como uma sequência fixa)

  • $file == *_${testseq}_*(aqui $testseqconsiderado como um padrão).

Ou o _imediatamente após o nome da variável será considerado como parte do nome da variável (é um caractere válido em um nome de variável).

RomanPerekhrest
fonte
Resposta correta, como se aplica ao OP, mas sem ser portátil. (Isso não é crítica à resposta fornecida, apenas um aviso aos leitores). ;-)
Cbhihe
28

Use o =~operador para fazer comparações de expressões regulares:

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

Dessa forma, ele irá comparar se $filetiver $testseqem seu conteúdo.

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

Se eu mudar testseq="Const":

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

Mas tenha cuidado com o que você alimenta $testseq. Se a string nela mostrar um regex (como [0-9]por exemplo), há mais chances de acionar uma "correspondência".

Referência :


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

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

Usar case ... esacé uma das maneiras mais simples de executar uma correspondência de padrões de maneira portátil. Ele funciona como uma declaração de "switch" em outros idiomas ( bash, zshe ksh93também permite que você faça queda-through de várias maneiras incompatíveis). Os padrões usados ​​são os padrões de globbing do nome do arquivo padrão.

O problema que você está enfrentando se deve ao fato de _ser um caractere válido em um nome de variável. O shell será visto *_$testseq_*como " *_seguido pelo valor da variável $testseq_e um *". A variável $testseq_é indefinida, portanto ela será expandida para uma sequência vazia e você terminará com *_*, o que obviamente corresponde ao $filevalor que você possui. Você pode esperar obter Trueo nome do nome do arquivo $filecontido em pelo menos um sublinhado.

Para delimitar corretamente o nome da variável, o uso "..."em torno da expansão: *_"$testseq"_*. Isso usaria o valor da variável como uma sequência. Deseja usar o valor da variável como um padrão , use em *_${testseq}_*vez disso.

Outra solução rápida é incluir os sublinhados no valor de $testseq:

testseq="_gen_"

e use apenas *"$testseq"*como padrão (para uma comparação de cadeias).

Kusalananda
fonte
Portanto, o shell procurará uma variável $ testseq_ e não a encontrará e a substituirá por uma string vazia.
Viesturs 13/06
@ Viesturs Esse é o cerne da questão, sim.
Kusalananda
1
Para uma pesquisa de substring, deve ser *"$testseq"*para caselike [[...]](exceto zsh, a menos que você ative o globsubst)
Stéphane Chazelas