⬆️ ⬇️

Frequent programming errors in Bash (end)

The end of the translation Bash Pitfalls . The previous parts are available in the “Shell” blog ( part 1 , part 2 ) and in my blog .



22. echo "Hello World!"



The problem is that in the Bash interactive shell, this command will cause an error:

  bash:! ": event not found 


This is because, with the default settings, Bash performs the csh-style command history substitution using an exclamation mark. There is no such problem in scripts, only in an interactive shell.



The obvious solution does not work here:

  $ echo "hi \!"
 hi \! 




You can enclose this string in single quotes:

  echo 'Hello World!' 


But the best solution here is to temporarily turn off the histexpand parameter. This can be done with the set +H or set +o histexpand :

  set + H
 echo "Hello World!" 


Why, then, do not always use single quotes? Imagine that you want to get information about mp3-files:

  mp3info -t "Don't Let It Show" ...
 mp3info -t "Ah! Leah!"  ... 


Single quotes are not appropriate here, since the names of the songs contain apostrophes in the names, and the use of double quotes will lead to a problem with the substitution of command history (and if the double quotes were also contained in the file names, it would have been completely obvious that). Since I personally (Greg Wooledge, author of the text) never use command history substitution, I just put the command set +H in my .bashrc. But this is a matter of habit and everyone decides for himself.

')

23. for arg in $ *



In Bash, as well as in other shells of the Bourne shell family, there is a special syntax for working with positional parameters in turn, but $* and $@ not exactly what you need: after substituting the parameters, they become a word list, passed in arguments, not a list of parameters separately.



Here is the correct syntax:

  for arg in "$ @" 


Or simply:

  for arg 


for arg matches for arg in "$@" . Enclosed in double quotes, the variable "$@" is special street magic, thanks to which each command line argument is enclosed in double quotes, so that it looks like a separate word. In other words, "$@" converted to a list of "$1" "$2" "$3" , etc. This trick will work in most cases.



Consider an example:

  #! / bin / bash
 # wrong
 for x in $ *;  do
 echo "parameter: '$ x'"
 done 


This code will print:

  $ ./myscript 'arg 1' arg2 arg3
 parameter: 'arg'
 parameter: '1'
 parameter: 'arg2'
 parameter: 'arg3' 


Here’s how it should look:

  #! / bin / bash
 # right!
 for x in "$ @";  do
     echo "parameter: '$ x'"
 done 




  $ ./myscript 'arg 1' arg2 arg3
 Parameter: 'arg 1'
 parameter: 'arg2'
 parameter: 'arg3' 


24. function foo ()



In some shells it works, but not in all. Never combine the function keyword with parentheses () when defining a function.



Some versions of bash allow you to use both function and () at the same time, but you cannot do that in any other shell. Some interpreters, however, will accept function foo , but for maximum compatibility it is better to use:

  foo () {
  ...
 } 


25. echo "~"



Replacing tilde (tilde expansion) occurs only when the ~ character is not surrounded by quotes. In this example, echo will print ~ to stdout instead of listing the user's home directory.



Escaping variables with paths that should be expressed relative to the home directory should be done using $HOME instead of ~ .

  "~ / dir with spaces" # "~ / dir with spaces"
 ~ "/ dir with spaces" # "~ / dir with spaces"
 ~ / "dir with spaces" # "/ home / my photos / dir with spaces"
 "$ HOME / dir with spaces" # "/ home / my photos / dir with spaces" 


26. local varname = $ (command)



When defining a local variable in a function, local itself works as a command. Sometimes it may in an incomprehensible way interact with the rest of the string. For example, if in the following command you want to get the return code ($?) Of the substituted command, you will not get it: the return code of the local command overlaps it.



Therefore, it is better to separate these commands:

  local varname
 varname = $ (command)
 rc = $? 

Source: https://habr.com/ru/post/48053/



All Articles