Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

quoting: $foo, $(foo), ${foo} ..

5 views
Skip to first unread message

David Kirkby

unread,
Jan 8, 2010, 1:59:35 AM1/8/10
to
I've seen alo of the following in scripts,

if [ $foo = "sun" ] && [ $bar = "moon" ] ; then

if [ "$foo" = "sun" ] && [ $bar = "moon" ] ; then

if [ "x$foo" = "xsun" ] && [ "x$bar" = "xmoon" ] ; then

if [ x$foo = "xsun" ] && [ x$bar = "xmoon" ] ; then

Also, sometime { and } are used.

What is the best way to compare things in shell scripts?

David W. Hodgins

unread,
Jan 8, 2010, 3:11:32 AM1/8/10
to
On Fri, 08 Jan 2010 01:59:35 -0500, David Kirkby <drki...@gmail.com> wrote:

> What is the best way to compare things in shell scripts?

Always quote variables, unless you have a good reason not to.

See http://tldp.org/LDP/abs/html/comparison-ops.html

Regards, Dave Hodgins

--
Change nomail.afraid.org to ody.ca to reply by email.
(nomail.afraid.org has been set up specifically for
use in usenet. Feel free to use it yourself.)

Stephane CHAZELAS

unread,
Jan 8, 2010, 3:21:52 AM1/8/10
to
2010-01-7, 22:59(-08), David Kirkby:

They are all wrong except for the 3rd as quotes are placed where
they're not necessary (arguments containing no special
characters) and not where they are necessary (around variables
to avoid special expansion (word splitting and globbing)).

[ "$foo" = sun ] && [ "$bar" = moon ]

will work in POSIX shells,

[ sun = "$foo" ] && [ moon = "$bar" ]


[ "x$foo" = xsun ] && [ "x$bar" = xmoon ]

[ sunmoon = "$foo$bar" ]
[ "x$foo$bar" = sunmoon ]

will work in all shells.

--
Stï¿œphane

Geoff Clare

unread,
Jan 8, 2010, 8:42:10 AM1/8/10
to
Stephane CHAZELAS wrote:

> [ "$foo" = sun ] && [ "$bar" = moon ]
>
> will work in POSIX shells,
>
> [ sun = "$foo" ] && [ moon = "$bar" ]
> [ "x$foo" = xsun ] && [ "x$bar" = xmoon ]
> [ sunmoon = "$foo$bar" ]
> [ "x$foo$bar" = sunmoon ]
>
> will work in all shells.

The last two will produce false positives if, for example,
foo=s and bar=unmoon. (Assuming the last is fixed to have
xsunmoon on the rhs.)

--
Geoff Clare <net...@gclare.org.uk>


David Kirkby

unread,
Jan 8, 2010, 12:38:42 PM1/8/10
to
On Jan 8, 8:21 am, Stephane CHAZELAS <stephane_chaze...@yahoo.fr>
wrote:
> Stéphane

Thank you Stephane. I wonder if you could answer the following
questions, as I'm puzzled why a script is not working as I believe it
should.

1) Can you tell me if the following is perfectly safe:

if [ ! -x "$SAGE_LOCAL/bin/testcc.sh" ] || [ ! -x "$SAGE_LOCAL/bin/
testcxx.sh" ] ; then
echo "testcc.sh and/or testcxx.sh either do not exist, or are not
executable"
exit 1
fi

$SAGE_LOCAL is a directory (in my case /export/home/drkirkby/
sage-4.3.1.alpha1/local). I happen to know that $SAGE_LOCAL will have
no spaces in it, but in general would the above work if the path has
spaces? Let's for example assume it was "/export/home/drkirkby/this is
an alpha release of sage 4.3.1". Can I be sure the above will always
work?

Is:

if [ ! -x ""$SAGE_LOCAL"/bin/testcc.sh" ] || [ ! -x ""$SAGE_LOCAL"/bin/
testcxx.sh" ] ; then
echo "testcc.sh and/or testcxx.sh either do not exist, or are not
executable"
exit 1
fi

better or not? If not, is there a better way, and if so how? (By
better, I want something that has the best probability of working in
any shell - bash, POSIX and preferably some of the more common non-
POSIX shells, though I appreciate one can't cover every possibility
there.)

2) As you might guess from the names, the scripts test the C and C++
compilers. They do it by detecting what macros are predefined. I do
happen to know the scripts will only print something without a space
(either GCC, Sun_Studio, HP_on_Tru64, HP_on_HP-UX, IBM_on_AIX,
HP_on_Alpha_Linux or Unknown). But in general, lets assume I did not
know this fact. How should I test if the two scripts are printing the
same thing?

If I run them at the command line:

drkirkby@hawk:~/sage-4.3.1.alpha1$ echo $CC
cc
drkirkby@hawk:~/sage-4.3.1.alpha1$ echo $CXX
CC
drkirkby@hawk:~/sage-4.3.1.alpha1$ local/bin/testcc.sh $CC
Sun_Studio
drkirkby@hawk:~/sage-4.3.1.alpha1$ local/bin/testcxx.sh $CXX
Sun_Studio

I tried to set two variables like this:

C_compiler=`"$(SAGE_LOCAL)"/bin/testcc.sh $CC`
C_PLUS_PLUS_compiler=`"$(SAGE_LOCAL)"/bin/testcxx.sh $CXX`


but that results in errors when the script is run.

/export/home/drkirkby/sage-4.3.1.alpha1/local/bin/sage-spkg: line 317:
SAGE_LOCAL: command not found
/export/home/drkirkby/sage-4.3.1.alpha1/local/bin/sage-spkg: line
317: /bin/testcc.sh: No such file or directory
/export/home/drkirkby/sage-4.3.1.alpha1/local/bin/sage-spkg: line 318:
SAGE_LOCAL: command not found
/export/home/drkirkby/sage-4.3.1.alpha1/local/bin/sage-spkg: line
318: /bin/testcxx.sh: No such file or directory


C_compiler="`$(SAGE_LOCAL)/bin/testcc.sh $CC`"
C_PLUS_PLUS_compiler="`$(SAGE_LOCAL)/bin/testcxx.sh $CXX`"

does not work. Neither does

C_compiler="`"$(SAGE_LOCAL)"/bin/testcc.sh $CC`"
C_PLUS_PLUS_compiler="`"$(SAGE_LOCAL)"/bin/testcxx.sh $CXX`"

The only thing I've found to semi work is:

C_compiler=`$SAGE_LOCAL/bin/testcc.sh $CC`
C_PLUS_PLUS_compiler=`$SAGE_LOCAL/bin/testcxx.sh $CXX`

but I'm concerned that in general that might not be reliable if
SAGE_LOCAL had spaces or other difficult characters.

Any ideas of the best way to do this?

3) Using

C_compiler=`$SAGE_LOCAL/bin/testcc.sh $CC`
C_PLUS_PLUS_compiler=`$SAGE_LOCAL/bin/testcxx.sh $CXX`
# Exit if there are a mixture of compilers.
if [ "$C_compiler" != "$C_PLUS_PLUS_compiler" ] ; then
echo ""
echo "ERROR: You have different C and C++ compilers."
echo "ERROR The C compiler is $C_compiler, the C++ compiler is
$C_PLUS_PLUS_compiler"
echo "ERROR: This mixture can not be used to build Sage"
echo "ERROR: Ensure both compilers are the same"
exit 1
fi

fails to produce the expected result, as the output from the script
is:

ERROR: You have different C and C++ compilers.
ERROR The C compiler is Sun_Studio, the C++ compiler is Sun_Studio
ERROR: This mixture can not be used to build Sage
ERROR: Ensure both compilers are the same

Any thoughts on how I can make this bullet proof?

Stephane CHAZELAS

unread,
Jan 8, 2010, 1:40:16 PM1/8/10
to
2010-01-8, 09:38(-08), David Kirkby:
[...]

> 1) Can you tell me if the following is perfectly safe:
>
> if [ ! -x "$SAGE_LOCAL/bin/testcc.sh" ] || [ ! -x "$SAGE_LOCAL/bin/
> testcxx.sh" ] ; then
> echo "testcc.sh and/or testcxx.sh either do not exist, or are not
> executable"
> exit 1
> fi

You can't get any safer than that.

However note that the files may exist and not be accessible (so
that the test above can't decide). So instead of "do not exist",
you could say "cannot be found".

Also, it's a good idea to output error messages on stderr, so:

echo >&2 "..."

> $SAGE_LOCAL is a directory (in my case /export/home/drkirkby/
> sage-4.3.1.alpha1/local). I happen to know that $SAGE_LOCAL will have
> no spaces in it, but in general would the above work if the path has
> spaces? Let's for example assume it was "/export/home/drkirkby/this is
> an alpha release of sage 4.3.1". Can I be sure the above will always
> work?

Yes, it shouldn't have any problem with any character, and if it
has, it's a bug and you can't get any better.

>
> Is:
>
> if [ ! -x ""$SAGE_LOCAL"/bin/testcc.sh" ] || [ ! -x ""$SAGE_LOCAL"/bin/
> testcxx.sh" ] ; then
> echo "testcc.sh and/or testcxx.sh either do not exist, or are not
> executable"
> exit 1
> fi
>
> better or not?

It's worse, it's "" followed by $SAGE_LOCAL unquoted, so subject
to word splitting and filename generation.

[...]


> C_compiler=`"$(SAGE_LOCAL)"/bin/testcc.sh $CC`
> C_PLUS_PLUS_compiler=`"$(SAGE_LOCAL)"/bin/testcxx.sh $CXX`
>
>
> but that results in errors when the script is run.
>
> /export/home/drkirkby/sage-4.3.1.alpha1/local/bin/sage-spkg: line 317:
> SAGE_LOCAL: command not found


$(...) is command substitution and is equivalent to `...` in
POSIX shells (you must be confusing with $(...) in Makefiles),
you want:

C_compiler=`"$SAGE_LOCAL"/bin/testcc.sh "$CC"`

(you've got no reason not to quote $CC as far as I can tell,
unless you want it to be split).

[...]


> C_PLUS_PLUS_compiler=`$SAGE_LOCAL/bin/testcxx.sh $CXX`
>
> but I'm concerned that in general that might not be reliable if
> SAGE_LOCAL had spaces or other difficult characters.

Only quoting prevents word splitting (or you could set $IFS to
the empty string), quoting also prevents filename generation
(which you can disable globally with "set -f"), and the removal
of empties (which you can't disable globally).

[...]


> if [ "$C_compiler" != "$C_PLUS_PLUS_compiler" ] ; then
> echo ""
> echo "ERROR: You have different C and C++ compilers."
> echo "ERROR The C compiler is $C_compiler, the C++ compiler is
> $C_PLUS_PLUS_compiler"

[...]


> fails to produce the expected result, as the output from the script
> is:

[...]


> ERROR The C compiler is Sun_Studio, the C++ compiler is Sun_Studio
> ERROR: This mixture can not be used to build Sage
> ERROR: Ensure both compilers are the same

[...]

Possibly $C_PLUS_PLUS_compiler has trailing spaces, or any one
of them have invisible characters. Try piping the output of the
script into "sed -n l"


--
Stï¿œphane

Rakesh Sharma

unread,
Jan 8, 2010, 4:19:31 PM1/8/10
to


case "$foo/$bar" in
'sun/moon' )
echo .............
;;
esac

bsh

unread,
Jan 8, 2010, 7:10:13 PM1/8/10
to
David Kirkby <drkir...@gmail.com> wrote:
> if [ $foo = "sun" ] && [ $bar = "moon" ] ; then
> if [ "$foo" = "sun" ] && [ $bar = "moon" ] ; then
> if [ "x$foo" = "xsun" ] && [ "x$bar" = "xmoon" ] ; then
> if [ x$foo = "xsun" ] && [ x$bar = "xmoon" ] ; then
> Also, sometime { and } are used.
> What is the best way to compare things in shell scripts?

I'm surprised that nobody thus so far has contributed
the obligatory comment, "Don't use the '[ ... ]' syntax."
You fail to give the shell you work in, and perhaps
this shell is indeed ksh(1), which means you can use
the newer "[[ ... ]]" syntax -- there is a good reason
why the older syntax is deprecated, and the new
one added!

See my previous post at:

http://groups.google.com/group/comp.unix.shell/msg/d3076c3ca825c1e3

There are unavoidable pathological cases involving
"[ ... ]" that are addressed in "[[ ... ]]", which
uses a different algorithm to discrimate parameters
versus operators. In _general_, double-quoting
parameters is no longer necessary, nor is explicit
variable substitution with integer-type variables.

So, the _best_ syntax is:

[[ $foo = sun && $bar = moon ]] && ...

BTW, the "=" operator takes a _regex_ as the RHS,
which allows much flexibility.

=Brian

Sven Mascheck

unread,
Jan 8, 2010, 7:42:56 PM1/8/10
to
bsh wrote:

> There are unavoidable pathological cases involving
> "[ ... ]" that are addressed in "[[ ... ]]", which
> uses a different algorithm to discrimate parameters
> versus operators.

I guess you mean "unavoidably" pathological
(the test operators -a and -o).

Otherwise all other shells would have had to "catch up"
long time ago... :-)

Chris F.A. Johnson

unread,
Jan 8, 2010, 8:16:34 PM1/8/10
to
On 2010-01-09, bsh wrote:
> David Kirkby <drkir...@gmail.com> wrote:
>> if [ $foo = "sun" ] && [ $bar = "moon" ] ; then
>> if [ "$foo" = "sun" ] && [ $bar = "moon" ] ; then
>> if [ "x$foo" = "xsun" ] && [ "x$bar" = "xmoon" ] ; then
>> if [ x$foo = "xsun" ] && [ x$bar = "xmoon" ] ; then
>> Also, sometime { and } are used.
>> What is the best way to compare things in shell scripts?
>
> I'm surprised that nobody thus so far has contributed
> the obligatory comment, "Don't use the '[ ... ]' syntax."

It's not obligatory at all; it's misguided.

> You fail to give the shell you work in, and perhaps
> this shell is indeed ksh(1), which means you can use
> the newer "[[ ... ]]" syntax -- there is a good reason
> why the older syntax is deprecated, and the new
> one added!

There is no good reason not to use the standard syntax; it is not
deprecated by any authority.

> See my previous post at:
>
> http://groups.google.com/group/comp.unix.shell/msg/d3076c3ca825c1e3
>
> There are unavoidable pathological cases involving
> "[ ... ]" that are addressed in "[[ ... ]]", which
> uses a different algorithm to discrimate parameters
> versus operators. In _general_, double-quoting
> parameters is no longer necessary, nor is explicit
> variable substitution with integer-type variables.
>
> So, the _best_ syntax is:
>
> [[ $foo = sun && $bar = moon ]] && ...
>
> BTW, the "=" operator takes a _regex_ as the RHS,
> which allows much flexibility.

99% of the time, file-globbing patterns are adequate, and they are
much more straightforward than regexes.

--
Chris F.A. Johnson, author <http://shell.cfajohnson.com/>
===================================================================
Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)
Pro Bash Programming: Scripting the GNU/Linux Shell (2009, Apress)
===== My code in this post, if any, assumes the POSIX locale =====
===== and is released under the GNU General Public Licence =====

David Kirkby

unread,
Jan 8, 2010, 9:49:15 PM1/8/10
to
On Jan 8, 6:40 pm, Stephane CHAZELAS <stephane_chaze...@yahoo.fr>
wrote:

> 2010-01-8, 09:38(-08), David Kirkby:
> [...]
>
> > 1) Can you tell me if the following is perfectly safe:
>
> > if [ ! -x "$SAGE_LOCAL/bin/testcc.sh" ] || [ ! -x "$SAGE_LOCAL/bin/
> > testcxx.sh" ] ; then
> >   echo "testcc.sh and/or testcxx.sh either do not exist, or are not
> > executable"
> >   exit 1
> > fi
>
> You can't get any safer than that.
>
> However note that the files may exist and not be accessible (so
> that the test above can't decide). So instead of "do not exist",
> you could say "cannot be found".

Thank you.

> Also, it's a good idea to output error messages on stderr, so:
>
> echo >&2 "..."

Cheers.


> [...]> if [ "$C_compiler" != "$C_PLUS_PLUS_compiler" ] ; then
> >     echo ""
> >     echo "ERROR: You have different C and C++ compilers."
> >     echo "ERROR  The C compiler is $C_compiler, the C++ compiler is
> > $C_PLUS_PLUS_compiler"
> [...]
> > fails to produce the expected result, as the output from the script
> > is:
> [...]
> > ERROR  The C compiler is Sun_Studio, the C++ compiler is Sun_Studio
> > ERROR: This mixture can not be used to build Sage
> > ERROR: Ensure both compilers are the same
>
> [...]
>
> Possibly $C_PLUS_PLUS_compiler has trailing spaces, or any one
> of them have invisible characters. Try piping the output of the
> script into "sed -n l"

Thank you. It was an extra space in the script being called. I'll get
that script changed. I can't work out why the script did this, but the
sed statement will go there - better to do it just once.

Thank you very much Stéphane - you have been most helpful. I was not
over impressed with that 'Advanced Bash-Scripting Guide' web page
someone pointed me at. I don't want to write for bash - I want to
write things that work with all shells (well, as many as possible).

bsh

unread,
Jan 13, 2010, 8:00:37 PM1/13/10
to
"Chris F.A. Johnson" <cfajohn...@gmail.com> wrote:
> bsh wrote:
> > David Kirkby <drkir...@gmail.com> wrote:
> > > What is the best way to compare things in shell scripts?
> > > ...

> > There is a good reason why the older syntax is deprecated,


> > and the new one added!

> There is no good reason not to use the standard syntax; it
> is not deprecated by any authority.

Chris, you didn't follow my link to read POSIX's own warning(s),
from:

http://www.opengroup.org/onlinepubs/009695399/utilities/test.html

If you had, you would have read:

APPLICATION USAGE:

Scripts should be careful when dealing with user-
supplied input that could be confused with primaries
and operators. Unless the application writer knows
all the cases that produce input to the script,
invocations like:

test "$1" -a "$2"

Should be written as:

test "$1" && test "$2"

To avoid problems if a user supplied values such as
$1 set to '!' and $2 set to the null string.
--

(I'm surprised that the above code ignores its own
recommendation to not use the implicit operator but instead
explicitly specify either "-n" or "-z").

I recommend reading this whitepaper for an in-depth discussion
of implementation detail, historical background, and problematic
syntax and cases. There are many nuances, including order-of-
precedence issues, that I was previously not aware of despite
many years of scripting experience and manpage reading -- for
instance, that "[[ ... ]]" was included in an early draft of the
POSIX standard!

=Brian

Chris F.A. Johnson

unread,
Jan 13, 2010, 8:31:25 PM1/13/10
to

That is the standard syntax I was referring to: test (a.k.a
[ ... ]) should be used instead of [[ ... ]].

> I recommend reading this whitepaper for an in-depth discussion
> of implementation detail, historical background, and problematic
> syntax and cases. There are many nuances, including order-of-
> precedence issues, that I was previously not aware of despite
> many years of scripting experience and manpage reading -- for
> instance, that "[[ ... ]]" was included in an early draft of the
> POSIX standard!

It's not there now, so there is no reason to use it.

0 new messages