-
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 $arg2In 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 expectedIf $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=$var1But 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
1234To 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
doneThis will return:
hello
world
this
is
JohnTo avoid that, you have to use quotes:
for a in "${array[@]}" ; do
echo $a
doneSee 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 spacesThe 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 OKif [ "$var" == "result" ] ; then
echo OK
fi
# even if var="result", $? ALWAYS = 0 because the if syntax was OKif [ "$(mycommand)" == "result" ] ; then
echo OK
fi
# even if mycommand fails, $? ALWAYS = 0 because the if syntax was OKif false ; then
echo OK
fi
# even if the if statement will always be false, $? ALWAYS = 0 because the if syntax was OKIf 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.
-
returnwithout argument will return the last command result (similar toreturn $?) -
exitwithout 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_commandIn 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 /pathEvery 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[@]}"