Using Quotes Makes Your Scripts More Robust
Many of those who are new to shell scripting often forget to quote strings or variables and hence embed bugs in their scripts. It makes things worse that these bugs are usually hard to reproduce though it is indeed very obvious by examining the scripts directly.
Quotes ensure strings recognized correctly
Without quotes, empty strings and strings with white-space cause problems.
[ "$VAR" == abc ] # OK [ $VAR == abc ] # OOPS # If VAR == "" # ==> [ == abc ] # ==> -bash: [: ==: unary operator expected # If VAR == "a b c" # ==> [ a b c = abc ] # bash: [: too many arguments
Quotes preserve white-space in strings
# ls | grep -i linux # OK files=$(ls) echo "$files" | grep -i linux # OK echo $files | grep -i linux # OOPS, everything is in the same line
Quotes prevent shell expansions and substitutions
This is based on a real-world bug. A person spent days trying to reproduce a bug but just all in vein. In fact the script is rather short and simple. When that guy turned to me at last, I figured out the root cause instantly by skiming the script. Obviously, the poor guy should had used quotes in his script.
cat input | tr [A-Z] [a-z] # OOPS
The issue here is that
[a-z] might be expanded by shell based on
file name globing. Hence the result of this command line depends files under
- If there is no file named by a single alphabet character, the command line works as intended.
- If there are some files of this pattern
- You'll get an error message if you are lucky. You are lucky in the sense
that you know there is something wrong. For instance, the actual command
get run after shell expansion might be this:
cat input | tr [A-Z] a b c # you are lucky as an error msg will
- There are also chances that the command still runs but not the way you
intended. This is a more dangerous situation since you are aware of the
error. For example, the actual command run might be:
show up ==> cat input | tr [A-Z] a # OOPS
This is a valid command, hence there will be no error message. But this is not what you want.
- You'll get an error message if you are lucky. You are lucky in the sense that you know there is something wrong. For instance, the actual command get run after shell expansion might be this:
The correct command is
cat input | tr '[A-Z]' '[a-z]' # OK cat input | tr 'A-Z' 'a-z' # In fact, this is the correct usage of 'tr'
When running this kind of commands remotely, it is even trickier as both local file names and remote file names may impact the result.
ssh 188.8.131.52 'echo "[abcd]"' # OK ssh 184.108.40.206 echo [abcd] # WRONG ssh 220.127.116.11 echo "[abcd]" # WRONG ssh 18.104.22.168 'echo [abcd]' # WRONG
Think about why the later three one are wrong.
When to use 'eval'
#!/bin/bash foo="hello world" bar="[$foo]" echo "$bar" # ==> [hello world], this is expected hello="echo $bar" $hello # ==> [hello world], spaces squeezed hello="echo '$bar'" $hello # ==> '[hello world]', still not work eval "$hello" # ==> [hello world], yes!
blog comments powered by Disqus