39

I have the code

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

I test if file contains "gen". The output is "False". Nice!

The problem is when I substitute "gen" with a variable testseq:

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

Now the output is "True". How could it be? How to fix the problem?

24

You need to interpolate the $testseq variable with one of the following ways:

  • $file == *_"$testseq"_* (here $testseq considered as a fixed string)

  • $file == *_${testseq}_* (here $testseq considered as a pattern).

Or the _ immediately after the variable's name will be taken as part of the variable's name (it's a valid character in a variable name).

  • Correct answer as it applies to OP, but w/o being portable. (This is no criticism of the provided answer, just a warning to readers). ;-) – Cbhihe Oct 7 '18 at 15:56
28

Use the =~ operator to make regular expression comparsions:

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

This way, it will compare if $file has $testseq on its contents.

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

If i change testseq="Const":

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

But, be careful with what you feed $testseq with. If the string on it somehows represent a regex(like [0-9] for example) there is more chance to trigger a "match".

Reference:

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

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

Using case ... esac is one of the simplest ways to perform a pattern match in a portable way. It works as a "switch" statement in other languages (bash, zsh, and ksh93 also allows you to do fall-through in various incompatible ways). The patterns used are the standard file name globbing patterns.

The issue you are having is due to the fact that _ is a valid character in a variable name. The shell will thus see *_$testseq_* as "*_ followed by the value of the variable $testseq_ and an *". The variable $testseq_ is undefined, so it will be expanded to an empty string, and you end up with *_*, which obviously matches the $file value that you have. You may expect to get True as long as the filename in $file contains at least one underscore.

To properly delimit the name of the variable, use "..." around the expansion: *_"$testseq"_*. This would use the value of the variable as a string. Would you want to use the value of the variable as a pattern, use *_${testseq}_* instead.

Another quick fix is to include the underscores in the value of $testseq:

testseq="_gen_"

and then just use *"$testseq"* as the pattern (for a string comparison).

  • So the shell will be looking for a variable $testseq_ and not find it and substitute it with an empty string. – Viesturs Jun 13 '17 at 14:39
  • @Viesturs That's is the heart of the issue, yes. – Kusalananda Jun 13 '17 at 14:39
  • 1
    For a substring search it should be *"$testseq"* for case like for [[...]] (except for zsh unless you enable globsubst) – Stéphane Chazelas Mar 28 at 11:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.