I am reading bash script I do not understand what is going there.
#!/bin/sh
[ x$1 = x ]
What is going on the second line and what [ x$1 = x ]
mean?
I am reading bash script I do not understand what is going there.
#!/bin/sh
[ x$1 = x ]
What is going on the second line and what [ x$1 = x ]
mean?
That checks that $1
is empty, though it should be quoted (identical to [ -z "$1" ]
). Some very old shells didn't handle empty strings properly, so writers of portable scripts adopted this style of checking. It hasn't been necessary for decades, but people still do it that way because people still do it that way.
[ x$1 = x ]
is still incorrect, but [ "x$1" = x ]
would be for shells that have an issue where $1
is !
or (
or -n
.... [ "" = "$1" ]
and case $1 in "")
would also be OK though.
– Stéphane Chazelas
Jun 16 '14 at 21:16
[ -z "$1" ]
and [ "$1" = "" ]
still don't work with the /bin/sh of Solaris 10, the former with dash-0.5.4.
– Stéphane Chazelas
Jun 16 '14 at 21:50
#!/bin/sh
in his script so he's obviously confused as to what bash
or sh
are or are meant to be.
– Stéphane Chazelas
Jun 17 '14 at 10:49
[ "$1" = "" ]
still doesn't work with its /bin/sh
(though you'd want to use /usr/xpg4/bin/sh
there, not /bin/sh
). dash was fixed in that regard in January 2009.
– Stéphane Chazelas
Jun 17 '14 at 10:54
Square brackets indicate a test, so [ x$1 = x]
without if
or something similar is meaningless, although syntactically ok.
It's meant to evaluate to true if x$1
expands to x
, and false otherwise, but since it's unquoted, if $1
is (e.g.) "hey x", the shell will will see x = x
, so this construction is still not safe.
The purpose of the x = x
check is to determine if a variable is empty. A more common way to do this would be to just use quotes:
if [ "$1" = "" ]; then
Bash test operators -z
and -n
can also be used, but they are less portable to other types of shells.1
The reason for the quotes, or the x$1
, is so that the left hand side does not expand to nothing, which would be a syntactical error:
if [ = x ] # No good!
if [ "" = "" ] # Okay.
if [ x = x ] # Also okay.
1. Actually, test
can be standalone utility but most shells implement it as a built-in; check the difference between which test
and type test
. On GNU/Linux man test
claims to refer to the built-in, but if you call (e.g.) /usr/bin/test
, that utility seems to implement the features documented in the man page, including -z
and -n
.
[ x$1 = x ]
will also evaluate to true if $1
is for instance " -o x"
. Try sh -xc '[ x$1 = x ] && echo yes' sh ' -o x'
. [ x$1 = x ]
is wrong and doesn't make sense.
– Stéphane Chazelas
Jun 16 '14 at 22:23
if
to use test
, you can use it before &&
, ||
or after while
or examine the result using $?
– Jasen
Dec 14 '15 at 20:44
[ x$1 = x ]
Only makes sense in zsh
. That compares the concatenation of x
with the first argument of the script to x
. So the [
command returns true if $1
is empty or not provided.
[ $1 = "" ]
Wouldn't work because, in zsh
when an empty variable is not quoted in list contexts, it expands to no argument at all instead of an empty argument, so if $1
were unset or empty, the [
command would only receive as arguments [
, =
, the empty string and ]
which it couldn't make sense out of. [ -z "$1" ]
or [ "$1" = "" ]
would be OK though like in POSIX shells.
In Bourne-like/POSIX shells, [ x$1 = x ]
does not make sense. That's the split+glob operator somehow applied to the concatenation of x
and the first argument of the script hoping that the result and =
and x
, and ]
make up a valid test expression for the [
command.
For instance, if the script was passed one " = x -o x ="
argument, [
would receive those arguments: [
, x
, =
, x
, -o
, x
, =
, x
, ]
, which [
would understand as comparing x
with x
and x
with x
and return true.
If $1
were "* *"
, then the shell would pass to the [
command the list of files in the current directory whose name starts with x
(the glob expansion of x*
), then the list of non-hidden files (expansion *
)... which [
is unlikely to be able to make any sense out of. The only cases where that would do anything sensible is if $1
does not contain wildcard or blank characters.
Now, what you sometimes find is code like:
[ "x$1" = x ]
That is used to test if $1
is empty or unset.
The normal way to test for an empty or unset variable is:
[ -z "$1" ]
But that fails for some values of $1
like =
in some (non-POSIX) [
implementations like the builtin one in the Bourne shell as found as /bin/sh
on Solaris 10 and before or some old versions of dash
(up to 0.5.4) or the sh
of some BSDs.
That's because [
sees [
, -z
, =
, ]
and complains about missing arguments to the =
binary operator instead of understanding it as the -z
unary operator applied to the =
string.
Similarly, [ "$1" = "" ]
fails for some implementations of [
if $1
is !
or (
.
In those shell/[
implementations:
[ "x$1" = x ]
is always a valid test regardless of the value of $1
, so are:
[ "" = "$1" ]
and:
[ -z "${1:+x}" ]
and
case $1 in "") ...; esac
Of course, if you want to check that no argument is provided, you'd do:
[ "$#" -eq 0 ]
That is, you check the number of arguments passed to the script.
Note that nowadays, [ -z "$var" ]
is clearly specified by POSIX and cannot fail in conformant [
implementations (and bash
's [
is and has been for decades). So you should be able to rely on it in POSIX sh or bash
scripts.
x$1
is concatenating two string x
and $1
and if $1 is empty, x$1 equals x, and [ x$1 = x ] will be true as a result. x = y
is used to compare string in sh
x$1
is not quoted, so splitting and globbing is performed on those.
– Stéphane Chazelas
Jun 16 '14 at 22:22
[ x$1 = x ]
is true if $1
is unset/null/empty or not.
Try yourself with:
TEST= ;[ x$TEST = x] && echo "TEST is unset"
and
TEST=lolz ;[ x$TEST = x ] && echo "TEST is unset"
[ x$1 = x ]
is also true if $1
is for instance " -o x"
. Try sh -xc '[ x$1 = x ] && echo yes' sh ' -o x'
. [ x$1 = x ]
is wrong and doesn't make sense.
– Stéphane Chazelas
Jun 16 '14 at 22:22
Accepted answer is correct. It is just to note that the syntax is much shorter to compare potentially empty result of a command with some expected output:
if [ "x$(some_app)" = "xsome_result" ]
vs
res="$(some_app)"
if [ -n "$res" ] && [ "$res" = "some_result" ]
[ -n "$res" ] && [ "$res" = "some_result" ]
, [ -n "$res" ]
is redundant as if $res
is some_result
it's obviously non-empty. The accepted answer is incorrect btw in that the problem is not about empty values. See comments underneath and my answer.
– Stéphane Chazelas
Dec 22 '21 at 12:42
some_result
them why test
(if
) at all?
– Martian2020
Dec 22 '21 at 12:58
#!/bin/sh
shebang is ash
script, not abash
script obviously. – Stéphane Chazelas Aug 22 '23 at 07:32