Common Bash programming errors (end)
- Transfer
The end of the translation of Bash Pitfalls . The previous parts are available on the Shells blog ( part 1 , part 2 ) and on my blog .
The problem is that in the Bash interactive shell this command will throw an error:
This is because, at default settings, Bash performs 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:
You can enclose this string in single quotes:
But the most suitable solution here is to temporarily turn off the parameter
Why then not always use single quotes? Imagine that you want information about mp3 files:
Single quotes are not suitable here, since the song names contain apostrophes in the names, and the use of double quotes will lead to a problem with the substitution of the history of commands (and if double quotes were also contained in the file names, it would be generally a damn thing). Since I personally (Greg Wooledge, author of the text) never use command history substitution, I just put the command
In Bash, just like in other shells of the Bourne shell family, there is a special syntax for working with positional parameters in turn, but
Here is the correct syntax:
Or simply:
street magic, thanks to which every command line argument is enclosed in double quotes, so that it looks like a separate word. In other words,
Consider an example:
This code will print:
Here's how it should look:
In some shells this works, but not in all. Never combine a keyword
Some versions of bash allow you to use both
A tilde expansion only happens when the ~ character is not surrounded by quotation marks. This example
Escaping variables with paths that must be expressed relative to the home directory should be done using
By defining a local variable in a function, it
Therefore, these commands are best separated:
22. echo "Hello World!"
The problem is that in the Bash interactive shell this command will throw an error:
bash:! ": event not found
This is because, at default settings, Bash performs 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 most suitable solution here is to temporarily turn off the parameter
histexpand
. This can be done with the command set +H
or set +o histexpand
:set + H echo "Hello World!"
Why then not always use single quotes? Imagine that you want information about mp3 files:
mp3info -t "Don't Let It Show" ... mp3info -t "Ah! Leah!" ...
Single quotes are not suitable here, since the song names contain apostrophes in the names, and the use of double quotes will lead to a problem with the substitution of the history of commands (and if double quotes were also contained in the file names, it would be generally a damn thing). 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, just like in other shells of the Bourne shell family, there is a special syntax for working with positional parameters in turn, but
$*
also $@
not quite what you need: after parameter substitution, they become a list of words passed in arguments, rather than a list of options individually. Here is the correct syntax:
for arg in "$ @"
Or simply:
for arg
for arg
matches for arg in "$@"
. A variable enclosed in double quotes "$@"
is a special "$@"
converted to a list "$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 this works, but not in all. Never combine a keyword
function
with brackets () when defining a function. Some versions of bash allow you to use both
function
, and ()
, at the same time , but you cannot do this in any other shell. Some interpreters, however, will perceive function foo
, but for maximum compatibility it is better to use:foo () { ... }
25. echo "~"
A tilde expansion only happens when the ~ character is not surrounded by quotation marks. This example
echo
will output ~
to stdout, instead of listing the user home directory. Escaping variables with paths that must be expressed relative to the home directory should be done using
$HOME
instead ~
."~ / 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)
By defining a local variable in a function, it
local
itself works as a command. Sometimes this may inexplicably interact with the rest of the string. For example, if in the next command you want to receive the return code ($?) Of the substituted command, you will not receive it: the return code of the local command overrides it. Therefore, these commands are best separated:
local varname varname = $ (command) rc = $?