Recently I've got confused by the following situation.
What is the difference between two if usage:
Case 1
amount=10
if [[ $amount -eq 10 ]]
then
echo "something"
fi
script output:
$ ./1.sh
something
Case 2
if [[ amount -eq 10 ]]
This also works like this (note that the variable name doesn't contain the $).
So the question is how does it work even without dollar sign in the variable name.
P.S. I'm using a POSIX shell on HP-UX.
man bash
ARITHMETIC EVALUATION
...
Shell variables are allowed as operands; parameter expansion is per‐
formed before the expression is evaluated. Within an expression,
shell variables may also be referenced by name without using the
parameter expansion syntax.
In this context shell does not expect anything but numerics, so it expands strings as variables. That makes sense to me.
Related
Let's suppose that you have a variable which is subject to word splitting, globing and pattern matching:
var='*
.?'
While I'm pretty sure that everyone agrees that "$var" is the best way to expand the variable as a string literal, I've identified a few cases where you don't need to use the double quotes:
Simple assignment: x=$var
Case statement: case $var in ...
Leftmost part of bash test construct: [[ $var .... ]]
UPDATE1: Bash here-string: <<< $var which works starting from bash-4.4 (thank you #GordonDavidson)
UPDATE2: Exported assignment (in bash): export x=$var
Is it correct? Is there any other shell/bash statement where the variable isn't subject to glob expansion or word splitting without using double-quotes? where expanding a variable with or without double quotes is 100% equivalent?
The reason why I ask this question is that when reading foreign code, knowing the above mentioned border-cases might help.
For example, one bug that I found in a script that I was debugging is something like:
out_exists="-f a.out"
[[ $out_exists ]] && mv a.out prog.exe
mv: cannot stat ‘a.out’: No such file or directory
This question is a duplicate of What are the contexts where Bash doesn't perform word splitting and globbing?, but that was closed before it was answered.
For a thorough answer to the question see the answer by Stéphane Chazelas to What are the contexts where Bash doesn't perform word splitting and globbing? - Unix & Linux Stack Exchange. Another good answer is in the "Where you can omit the double quotes" section in the answer by Gilles to When is double-quoting necessary? - Unix & Linux Stack Exchange.
There seem to be a small number of cases that aren't covered by the links above:
With the for (( expr1 ; expr2 ; expr3 )) ... loop, variable expansions in any of the expressions inside the (( ... )) don't need to be quoted.
Several of the expansions described in the Shell Parameter Expansion section of the Bash Reference Manual are described with a word argument that isn't subject to word splitting or pathname expansion (globbing). Examples include ${parameter:-word}, ${parameter#word}, and ${parameter%word}.
Great question! If you need to word split a variable, the quotes should be left off.
If I think of other cases, I'll add to this.
var='abc xyz'
set "$var"
echo $1
abc xyz
set $var
echo $1
abc
I have come across an assignment like this which I have never seen before, ": ${var=$*}". The assignment can also be done like var=$*, but can anyone explain about the above what is being done. I tried to search for it but got nothing.
Explication:
For example:
A="value1"
B=${A="value2"}
echo $B -> value1
Now, when the variable A is not defined, it retrieves the value 'value2'
unset A
B=${A="value2"}
echo $B -> value2
Lets look at this line step by step:
: argument : This only executes the expansions of argument. The colon command is generally not useful, but can be used for parameter validation or initialisation via parameter expansion. It is also used to run infinite while loops.
What is the purpose of the : (colon) GNU Bash builtin?
[U&L] What purpose does the colon builtin serve
BashFAQ035
${var=word} The argument in the above expansion is of the form ${var=word}. This is identical to ${var:=word} with the only difference that the former tests if var is unset while the latter tests if var is unset or null. If the condition applies, var is assigned with the value of word
$* The value of word in the above is now the expansion of $*. It expands to a single string of the form $1c$2c$3c...$ where $n are the values of the command arguments and the value of c expands to the first character of the variable IFS.
Having this all said, this command is equivalent to the following line which uses classic programming jargon:
if [ -z ${var+x} ]; then var="$*"; fi
See How to check if a variable is set in Bash?
I'd like to have some explanation on how to best use variables in bash conditional expressions [[...]].
I usually write if statement in this way:
var=1;
# some code...
if [[ $var -eq 1 ]]; then echo "ok"; else echo "fail"; fi
and this return ok as I expected.
Now I saw in some script the same similar statement like:
var=1;
# some code...
if [[ var -eq 1 ]]; then echo "ok"; else echo "fail"; fi
The only difference is the missing parameter expansion character $ in the conditional expression [[...]].
I actually expected this statement to give an error, but this syntax is accepted and returns the ok string.
I tested this statement using bash (GNU bash, version 4.3.46), zsh (5.1.1), ksh (93u+ 2012-08-01) and busybox ash (BusyBox v1.23.2).
I only get an error with busybox shell:
ash: var: bad number
I saw in the bash man page, in the ARITHMETIC EVALUATION paragraph, that:
Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax
But I didn't find anything special related to parameter expansion in the CONDITIONAL EXPRESSIONS paragraph.
So, should conditional expression contain $ when referring to variable or not? and why?
The trigger here is -eq; since it is defined to perform integer comparison, its operands are evaluated in an arithmetic context. This isn't explicitly documented, though.
You should use the $, though. [[ is an extension, so there is no guarantee that it will behave identically in every shell that defines such a construct. In fact, I wouldn't even assume that [[ var -eq 3 ]] will continue to behave this way in future versions of the same shell. (( var == 3 )) is, though, documented to perform expansion of var since you are in a explicit arithmetic context.
Check the bash man page's sections on Compound Commands. In particular, the following:
((expression))
The expression is evaluated according to the rules described below
under ARITHMETIC EVALUATION. If the value of the expression is non-zero,
the return status is 0; otherwise the return status is 1. This is exactly
equivalent to `let "expression"`.
[[ expression ]]
Return a status of 0 or 1 depending on the evaluation of the conditional
expression expression. Expressions are composed of the primaries
described below under CONDITIONAL EXPRESSIONS.
If you are evaluating things that require arithmetic, use arithmetic evaluation, and check the CONDITIONAL EXPRESSIONS section for the various things you can do with [[ ... ]]. Conditions in double-square-brackets can evaluate both strings and integers, and sometimes those work the same way .. sometimes not.
From the bash man page, under CONDITIONAL EXPRESSIONS:
string1 == string2
string1 = string2
True if the strings are equal. = should be used with the test command for POSIX conformance. When used with the [[ command, this performs pattern
matching as described above (Compound Commands).
...
arg1 OP arg2
OP is one of -eq, -ne, -lt, -le, -gt, or -ge. These arithmetic binary operators return true if arg1 is equal to, not equal to, less than, less than
or equal to, greater than, or greater than or equal to arg2, respectively. Arg1 and arg2 may be positive or negative integers.
Obviously, the string "1" is the string "1", so if n=1, and you compare $n against the string "1", you'll get a hit. But you should know what you're doing, and that this is not a numeric comparison. And similarly, < and > are NOT numeric comparisons, they are string comparisons. So while "1" < "2", you may be surprised that "11" < "2" as well.
That said, bash is forgiving about what kind of conditions you ask it to evaluate:
bash-4.4$ n=1
bash-4.4$ [[ n -eq 1 ]] && echo yes
yes
bash-4.4$ [[ $n -eq 1 ]] && echo yes
yes
bash-4.4$ (( n == 1 )) && echo yes
yes
bash-4.4$ (( n = 2 )) && echo yes
yes
bash-4.4$ echo "$n"
2
The first one works because n can't be anything but a variable in this context, so bash treats it as such. But you shouldn't rely on this behaviour. Use dollar signs for variables in conditional expressions, and stick with the bash mantra, "always quote your variables".
Inside a double-square-bracket expression in bash, you should use the arithmetic binary operators if you intend your comparison to be of integers.
Note that your busybox build appears to be using ash, which is NOT bash. Ash is the "Almquist shell", an older POSIX shell than bash, written in the late 1980s. Ash is the basis for /bin/sh in FreeBSD, in addition to being preferred often over bash in embedded systems (hence busybox) due to its smaller size.
You might want to consider writing POSIX shell scripts instead of Bash shell scripts. This will mean simplifying some things, as well as jumping through some hoops for others. POSIX does not include double-square-bracket expressions, but it does allow things like [ "$n" -eq 1 ] or [ $(( n + 1 )) -eq 2 ]. And it'll work in busybox. :)
I was running some Bash conditional scripts and discovered if I run this:
#!/bin/bash
read foo
if [[ foo -eq 1 ]]; then
echo "A"
fi
if [[ foo -eq 2 ]]; then
echo "B"
fi
The conditionals work fine under Bash 4.2.25 without the use of $foo. Why does this work without referencing the variable with a $?
From the description of bash Conditional Constructs, it says that [[ expression ]] performs arithmetic expansion of the expression. If you then find the section on Shell Arithmetic it says:
Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax.
"parameter expansion syntax" refers to putting a $ before the name.
I'm studyng Bash, and I see that the form
C=example
echo "$C"
give the same result of the form
C="example"
echo $C
I'd like to know if is better put the " " in the assignment of the variable or after the $. Or if it is indifferent. or if one is consider "more beautiful" than the other.
If you're certain that a variable's value is a single word (no white space) then it's OK to use $varname or ${varname}. If you can't guarantee this, then you should use "$varname" or "${varname}". Note that bash does word-splitting before interpreting your command, so you may actually get a syntax error if you don't quote the expression, for example
C="white space"
if [ -z $C ]
then
...
fi
will result in syntax error:
-bash: [: white: binary operator expected
while this works fine:
C="white space"
if [ -z "$C" ]
then
...
fi
This is due to the fact after variable expansion in the first, unquoted case bash sees this:
if [ -z white space ]
then
...
fi
and the -z operator expects just one, not two arguments. In the second, quoted case bash sees this:
if [ -z "white space" ]
then
...
fi
i.e. just a single argument as required. Note also that quotes were used in assignment
C="white space"
as it would also produce an error if you wrote
C=white space
since this would mean: execute command space with environment containing an added variable C=white.
So, in general you should quote these expressions to ensure your code is more robust against unforeseen variable values. This is especially true if the variable value comes from input, file etc. It is usually safe to drop the quotes for integer variables or when you just want to display the value of a variable as in echo $C.
It matters when the string contains whitespace characters. Without the quotes, whitespace characters are treated as token delimiters and bash tries to interpret the substituted string as an expression.
Always put quotes to be safe, when you don't intend to evaluate the variable as a part of the expression.
Imagine you change the input from "example" to "two words", then you could encounter strange behaviour or even syntax errors when executing the script, in case you have overlooked the above.
In other words,
C="abc def"
# the echo command receives one argument: "abc def"
echo "$C"
# echo receives two arguments: "abc" and "def"
echo $C
# bash tries to execute the program "abc" with a first argument "def"
$C
# bash tries to execute the program "abc def"
"$C"
A good documentation about quotes and word-spliting :
"USE MORE QUOTES!" They are vital. Also, learn the difference between ' and " and `. See http://mywiki.wooledge.org/Quotes and http://wiki.bash-hackers.org/syntax/words
greybot sample from IRC freenode #bash is talking to the world =)
If it's a one-word constant, it's irrelevant.
However, you should read about the two kinds of quoting. Try this article and this documentation. There is also a SO question.
Try with a real example with whitespace. For the string example you do not need any quoting at all. So create a file called This is an example.txt and then retry. Substitute echo with ls...