0

My Bash-Script has to do something different, according of the beginning of a Variable. When I run the following script:

#!/bin/bash

line="__B something"

if [ -n $(echo "$line" | grep "^__A") ]; then
   echo "Line __A"
elif [ -n $(echo "$line" | grep "^__B") ]; then
   echo "Line __B"
elif [ -n $(echo "$line" | grep "^__C") ]; then
   echo "Line __C"
elif [ -n $(echo "$line" | grep "^__D") ]; then
   echo "Line __D"
elif [ -n $(echo "$line" | grep "^__E") ]; then
   echo "Line __E"
elif [ $(echo "$line" | grep "^__F") ]; then
   echo -n "Line __F"
else
   echo "something other"
fi

Bash does not recognize that the String start's with __B.:

Output is:

Line __A

What's wrong with my Script? Thank you for your help!

2 Answers2

2

If you run your script using the xtrace option than you can see what is happening:

bash -x your-script
+ line='__B something'
++ echo '__B something'
++ grep '^__A'
+ '[' -n ']'
+ echo 'Line __A'
Line __A

Because you use the old test built-in (otherwise known as [) the expansion is done but results in nothing to test except the -n. Using the [[ keyword will quote things correctly:

bash -x your-script
+ line='__B something'
++ echo '__B something'
++ grep '^__A'
+ [[ -n '' ]]
++ echo '__B something'
++ grep '^__B'
+ [[ -n __B something ]]
+ echo 'Line __B'
Line __B

However, using the external program grep is a waste. Recent versions of bash have regular expressions (REs) built-in (using the binding operator =~) but you don't need REs in this case, simple globbing will do:

#!/bin/bash

line="__B something"

if [[ $line == __A* ]]; then
   echo "Line __A"
elif [[ $line == __B* ]]; then
   echo "Line __B"
elif [[ $line == __C* ]]; then
   echo "Line __C"
elif [[ $line == __D* ]]; then
   echo "Line __D"
elif [[ $line == __E* ]]; then
   echo "Line __E"
elif [[ $line == __F* ]]; then
   echo -n "Line __F"
else
   echo "something other"
fi
cdarke
  • 42,728
  • 8
  • 80
  • 84
1

I believe it has to do with the subtleties of the difference between using [[ and [ in bash if conditionals.

You can either use [[ and ]] everywhere (seems to work), e.g.

if [[ -n $(echo "$line" | grep "^__A") ]]; then
   echo "Line __A"

or you can quote the subshell like this

if [ -n "$(echo '$line' | grep '^__A')" ]; then
   echo "Line __A"

[[ has fewer surprises and is generally safer to use. But it is not portable - Posix doesn't specify what it does and only some shells support it (beside bash, i heard ksh supports it too). For example, you can do

[[ -e $b ]]

to test whether a file exists. But with [, you have to quote $b, because it splits the argument and expands things like "a*" (where [[ takes it literally). That has also to do with how [ can be an external program and receives its argument just normally like every other program (although it can also be a builtin, but then it still has not this special handling).

according to this answer here on stackoverflow. So in your case, most likely bash is separating your string on a space and that is having some side effects.

Community
  • 1
  • 1
Matthias
  • 3,160
  • 2
  • 24
  • 38