I tried the following script
#!/bin/bash
var1="Test 1"
var2="Test 2"
if [ "$var1"="$var2" ]
then
echo "Equal"
else
echo "Not equal"
fi
It gave me Equal. Although it should have printed Not equal
Only when I inserted space around = it worked as intended
if [ "$var1" = "$var2" ]
and printed Not equal
Why is it so? Why "$var1"="$var2" is not same as "$var1" = "$var2"?
Moreover, when I wrote if [ "$var1"= "$var2" ], it gave
line 4: [: Test 1=: unary operator expected
What does it it mean? How come its expecting unary operator?
test (or [ expr ]) is a builtin function. Like all functions in bash, you pass its arguments as whitespace separated words.
As the man page for bash builtins states: "Each operator and operand must be a separate argument."
It's just the way bash and most other Unix shells work.
Variable assignment is different.
In bash a variable assignment has the syntax: name=[value]. You cannot put unquoted spaces around the = because bash would not interpret this as the assignment you intend. bash treats most lists of words as a command with parameters.
E.g.
# call the command or function 'abc' with '=def' as argument
abc =def
# call 'def' with the variable 'abc' set to the empty string
abc= def
# call 'ghi' with 'abc' set to 'def'
abc=def ghi
# set 'abc' to 'def ghi'
abc="def ghi"
When the shell reads
if [ "$var1" = "$var2" ]
it invokes the command [ with 4 arguments. Whether [ is a builtin or an external command is irrelevant, but it may help to understand that it may be the external command /bin/[. The second argument is the literal '=' and the fourth is ']'. However, when the shell reads
if [ "$var1"= "$var2" ]
[ only gets 3 arguments: the expansion of $var1 with '=' appended, the expansion of $var2, and ']'. When it gets only 3 arguments, it expects the last argument to be ']' and the first argument to be a unary operator.
To add to the existing explanation, "$var1"="$var2" is just a single non-empty string, and thus always evaluates as true in a conditional.
[ "$var1"="$var2" ] && echo true
The above command will always print out true (even if var1 and var2 be empty).
In bash the best is to use [[ ]]:
x="test"
y="test"
if [[ "${x}" = "${y}" ]]; then
echo "Equals"
else
echo "No equals"
fi
Related
I tried the following script
#!/bin/bash
var1="Test 1"
var2="Test 2"
if [ "$var1"="$var2" ]
then
echo "Equal"
else
echo "Not equal"
fi
It gave me Equal. Although it should have printed Not equal
Only when I inserted space around = it worked as intended
if [ "$var1" = "$var2" ]
and printed Not equal
Why is it so? Why "$var1"="$var2" is not same as "$var1" = "$var2"?
Moreover, when I wrote if [ "$var1"= "$var2" ], it gave
line 4: [: Test 1=: unary operator expected
What does it it mean? How come its expecting unary operator?
test (or [ expr ]) is a builtin function. Like all functions in bash, you pass its arguments as whitespace separated words.
As the man page for bash builtins states: "Each operator and operand must be a separate argument."
It's just the way bash and most other Unix shells work.
Variable assignment is different.
In bash a variable assignment has the syntax: name=[value]. You cannot put unquoted spaces around the = because bash would not interpret this as the assignment you intend. bash treats most lists of words as a command with parameters.
E.g.
# call the command or function 'abc' with '=def' as argument
abc =def
# call 'def' with the variable 'abc' set to the empty string
abc= def
# call 'ghi' with 'abc' set to 'def'
abc=def ghi
# set 'abc' to 'def ghi'
abc="def ghi"
When the shell reads
if [ "$var1" = "$var2" ]
it invokes the command [ with 4 arguments. Whether [ is a builtin or an external command is irrelevant, but it may help to understand that it may be the external command /bin/[. The second argument is the literal '=' and the fourth is ']'. However, when the shell reads
if [ "$var1"= "$var2" ]
[ only gets 3 arguments: the expansion of $var1 with '=' appended, the expansion of $var2, and ']'. When it gets only 3 arguments, it expects the last argument to be ']' and the first argument to be a unary operator.
To add to the existing explanation, "$var1"="$var2" is just a single non-empty string, and thus always evaluates as true in a conditional.
[ "$var1"="$var2" ] && echo true
The above command will always print out true (even if var1 and var2 be empty).
In bash the best is to use [[ ]]:
x="test"
y="test"
if [[ "${x}" = "${y}" ]]; then
echo "Equals"
else
echo "No equals"
fi
I tried the following script
#!/bin/bash
var1="Test 1"
var2="Test 2"
if [ "$var1"="$var2" ]
then
echo "Equal"
else
echo "Not equal"
fi
It gave me Equal. Although it should have printed Not equal
Only when I inserted space around = it worked as intended
if [ "$var1" = "$var2" ]
and printed Not equal
Why is it so? Why "$var1"="$var2" is not same as "$var1" = "$var2"?
Moreover, when I wrote if [ "$var1"= "$var2" ], it gave
line 4: [: Test 1=: unary operator expected
What does it it mean? How come its expecting unary operator?
test (or [ expr ]) is a builtin function. Like all functions in bash, you pass its arguments as whitespace separated words.
As the man page for bash builtins states: "Each operator and operand must be a separate argument."
It's just the way bash and most other Unix shells work.
Variable assignment is different.
In bash a variable assignment has the syntax: name=[value]. You cannot put unquoted spaces around the = because bash would not interpret this as the assignment you intend. bash treats most lists of words as a command with parameters.
E.g.
# call the command or function 'abc' with '=def' as argument
abc =def
# call 'def' with the variable 'abc' set to the empty string
abc= def
# call 'ghi' with 'abc' set to 'def'
abc=def ghi
# set 'abc' to 'def ghi'
abc="def ghi"
When the shell reads
if [ "$var1" = "$var2" ]
it invokes the command [ with 4 arguments. Whether [ is a builtin or an external command is irrelevant, but it may help to understand that it may be the external command /bin/[. The second argument is the literal '=' and the fourth is ']'. However, when the shell reads
if [ "$var1"= "$var2" ]
[ only gets 3 arguments: the expansion of $var1 with '=' appended, the expansion of $var2, and ']'. When it gets only 3 arguments, it expects the last argument to be ']' and the first argument to be a unary operator.
To add to the existing explanation, "$var1"="$var2" is just a single non-empty string, and thus always evaluates as true in a conditional.
[ "$var1"="$var2" ] && echo true
The above command will always print out true (even if var1 and var2 be empty).
In bash the best is to use [[ ]]:
x="test"
y="test"
if [[ "${x}" = "${y}" ]]; then
echo "Equals"
else
echo "No equals"
fi
I tried the following script
#!/bin/bash
var1="Test 1"
var2="Test 2"
if [ "$var1"="$var2" ]
then
echo "Equal"
else
echo "Not equal"
fi
It gave me Equal. Although it should have printed Not equal
Only when I inserted space around = it worked as intended
if [ "$var1" = "$var2" ]
and printed Not equal
Why is it so? Why "$var1"="$var2" is not same as "$var1" = "$var2"?
Moreover, when I wrote if [ "$var1"= "$var2" ], it gave
line 4: [: Test 1=: unary operator expected
What does it it mean? How come its expecting unary operator?
test (or [ expr ]) is a builtin function. Like all functions in bash, you pass its arguments as whitespace separated words.
As the man page for bash builtins states: "Each operator and operand must be a separate argument."
It's just the way bash and most other Unix shells work.
Variable assignment is different.
In bash a variable assignment has the syntax: name=[value]. You cannot put unquoted spaces around the = because bash would not interpret this as the assignment you intend. bash treats most lists of words as a command with parameters.
E.g.
# call the command or function 'abc' with '=def' as argument
abc =def
# call 'def' with the variable 'abc' set to the empty string
abc= def
# call 'ghi' with 'abc' set to 'def'
abc=def ghi
# set 'abc' to 'def ghi'
abc="def ghi"
When the shell reads
if [ "$var1" = "$var2" ]
it invokes the command [ with 4 arguments. Whether [ is a builtin or an external command is irrelevant, but it may help to understand that it may be the external command /bin/[. The second argument is the literal '=' and the fourth is ']'. However, when the shell reads
if [ "$var1"= "$var2" ]
[ only gets 3 arguments: the expansion of $var1 with '=' appended, the expansion of $var2, and ']'. When it gets only 3 arguments, it expects the last argument to be ']' and the first argument to be a unary operator.
To add to the existing explanation, "$var1"="$var2" is just a single non-empty string, and thus always evaluates as true in a conditional.
[ "$var1"="$var2" ] && echo true
The above command will always print out true (even if var1 and var2 be empty).
In bash the best is to use [[ ]]:
x="test"
y="test"
if [[ "${x}" = "${y}" ]]; then
echo "Equals"
else
echo "No equals"
fi
prompt> if [ -f ]; then echo "true"; fi
true
Why does the if condition evaluate to true? Isn't -f supposed to need an argument?
prompt> if [ -f $1 ]; then echo "true"; fi
Does it mean I always need to check if the argument $1 is not empty before proceeding to the if condition?
The reason [ -f ] results in a successful exit code is because of this bit from the manual:
-n string
string
True if the length of string is non-zero.
Which is also explained here when talking about test/[:
The test and [ builtins evaluate conditional expressions using a set
of rules based on the number of arguments.
0 arguments
The expression is false.
1 argument
The expression is true if and only if the argument is not null.
2 arguments
If the first argument is ‘!’, the expression is true if and only if the second argument is null. If the first argument is one of the unary conditional operators (see Bash Conditional Expressions), the expression is true if the unary test is true. If the first argument is not a valid unary operator, the expression is false.
3 arguments
...
4 arguments
...
5 or more arguments
...
So while you expect to be using the two argument test the shell sees the one argument version and tests the string -f against the null string.
That is [ word ] is equivalent to [ -n word ].
You can avoid this problem by quoting your variable in the test:
if [ -f "$1" ]; then echo true; fi
In general you want to always quote variable expansions.
You can avoid that scenario using if [[ ]] syntax or simply by quoting the variable $1 in your current code.
Example:
if [ -f "$1" ]; then echo "true"; fi
Or:
if [[ -f $1 ]]; then echo "true"; fi
Note: Always try to quote variables.
I tried the following script
#!/bin/bash
var1="Test 1"
var2="Test 2"
if [ "$var1"="$var2" ]
then
echo "Equal"
else
echo "Not equal"
fi
It gave me Equal. Although it should have printed Not equal
Only when I inserted space around = it worked as intended
if [ "$var1" = "$var2" ]
and printed Not equal
Why is it so? Why "$var1"="$var2" is not same as "$var1" = "$var2"?
Moreover, when I wrote if [ "$var1"= "$var2" ], it gave
line 4: [: Test 1=: unary operator expected
What does it it mean? How come its expecting unary operator?
test (or [ expr ]) is a builtin function. Like all functions in bash, you pass its arguments as whitespace separated words.
As the man page for bash builtins states: "Each operator and operand must be a separate argument."
It's just the way bash and most other Unix shells work.
Variable assignment is different.
In bash a variable assignment has the syntax: name=[value]. You cannot put unquoted spaces around the = because bash would not interpret this as the assignment you intend. bash treats most lists of words as a command with parameters.
E.g.
# call the command or function 'abc' with '=def' as argument
abc =def
# call 'def' with the variable 'abc' set to the empty string
abc= def
# call 'ghi' with 'abc' set to 'def'
abc=def ghi
# set 'abc' to 'def ghi'
abc="def ghi"
When the shell reads
if [ "$var1" = "$var2" ]
it invokes the command [ with 4 arguments. Whether [ is a builtin or an external command is irrelevant, but it may help to understand that it may be the external command /bin/[. The second argument is the literal '=' and the fourth is ']'. However, when the shell reads
if [ "$var1"= "$var2" ]
[ only gets 3 arguments: the expansion of $var1 with '=' appended, the expansion of $var2, and ']'. When it gets only 3 arguments, it expects the last argument to be ']' and the first argument to be a unary operator.
To add to the existing explanation, "$var1"="$var2" is just a single non-empty string, and thus always evaluates as true in a conditional.
[ "$var1"="$var2" ] && echo true
The above command will always print out true (even if var1 and var2 be empty).
In bash the best is to use [[ ]]:
x="test"
y="test"
if [[ "${x}" = "${y}" ]]; then
echo "Equals"
else
echo "No equals"
fi