-
Notifications
You must be signed in to change notification settings - Fork 2
Bash tips and tricks
Develop in BASH can really be a nightmare if you are not aware of some details about it. Even if libbash.sh helps you a lot 😃, here are some tips and tricks you should know.
To go further, see the amazing tool Shellcheck.
-
Spaces and quotes
- Quotes in paths
- Quotes in arguments
- Quotes in comparison statements
- Quotes in variables attribution
- Quotes in parsing arguments
- Quotes in arrays
- Quotes in commands
- Simple quotes or double quotes?
-
Return codes
- The last command result
- Function returns
- return command VS exit command
- Arrays (coming soon...)
- Syntax
-
Running commands
- Commands and spaces
You should ALWAYS put double quotes around your variables! Let's see why:
If you make any operation on paths and files, put ALWAYS double quotes around them, or paths with spaces will fail to open. Example:
$ file="/path/file with spaces.txt"
$ cat $file
cat: /path/file does not exists!
cat: with does not exists!
cat: spaces.txt does not exists!
You should have done:
cat "$file"
Consider this example:
mycommand() {
first=$1
second=$2
...
}
arg1="argument with spaces"
arg2=1234
mycommand $arg1 $arg2
In this case, $first
has argument
and $second
has with
. To avoid this, call:
mycommand "$arg1" "$arg2"
Consider this example:
spaces=" "
if [ -z $spaces ] ; then ...
In this example, the $spaces
variable will be considered empty. So you should put quotes in comparison statements:
if [ -z "$spaces" ] ; then ...
Consider another example:
$ var=""
$ if [ $var == hello ] ; then ...
bash: [: == : unary operator expected
If $var
is empty, the if statement will fail with a bash crash (code 2) and will print and error on stderr.
With quotes, it will never crash, even if variable is empty:
$ if [ "$var" == hello ] ; then ...
Surprisingly, the case
statement does not need quotes:
var="some text"
case $var in ...
Variable attribution is a rare case where you don't need quotes. The following example is correct:
var1="variable with spaces"
var2=$var1
But if you cant to concatenate with another string, don't miss the quotes:
var1="variable with spaces"
var2="$var1 and something else"
Consider this example:
mycommand() {
for arg in $* ; do
echo $arg
done
}
arg1="argument with spaces"
arg2=1234
mycommand "$arg1" "$arg2"
It will return:
argument
with
spaces
1234
To avoid that, you have to use "$@"
instead of $*
:
mycommand() {
for arg in "$@" ; do
echo $1
done
}
Consider this example:
array=("hello world" "this is John")
for a in ${array[@]} ; do
echo $a
done
This will return:
hello
world
this
is
John
To avoid that, you have to use quotes:
for a in "${array[@]}" ; do
echo $a
done
See chapter Commands and spaces
in Running commands section.
You should prefer double quotes, but it depends what you want to do. While var="some text"
and var='some text'
does exactly the same, it will not work properly if you include variable names inside simple quotes:
$ var='some text'
# bad call
$ echo '$var with spaces'
$var with spaces
# good call
$ echo "$var with spaces"
some text with spaces
The last command result code (0-255) may have some unexpected values regarding context:
mycommand # $? = result of mycommand
var=$(mycommand) # $? = result of mycommand
local var=$(mycommand) # $? ALWAYS = 0 because local command is OK
if [ "$var" == "result" ] ; then
echo OK
fi
# even if var="result", $? ALWAYS = 0 because the if syntax was OK
if [ "$(mycommand)" == "result" ] ; then
echo OK
fi
# even if mycommand fails, $? ALWAYS = 0 because the if syntax was OK
if false ; then
echo OK
fi
# even if the if statement will always be false, $? ALWAYS = 0 because the if syntax was OK
If you don't call the return
command at the end of a function, the function will return the last command result (similar to return $?
). See above for more details about last command results.
-
return
without argument will return the last command result (similar toreturn $?
) -
exit
without argument will return code 0
Because of the spaces (again!), running commands can fail. See why below.
Consider this example:
rsync_command="rsync -e 'ssh -p 2222' myserver:/path /path"
# execute command
$rsync_command
In this example, the command will fail, because the -e
option has spaces inside it and bash execute it as:
rsync -e ssh -p 2222 myserver:/path /path
Every time you are running commands from variables, the only way to avoid spaces bugs is to use an array:
rsync_command=(rsync -e 'ssh -p 2222' myserver:/path /path)
# execute command; DO NOT FORGET THE QUOTES!
"${rsync_command[@]}"