for i in `ls * .mp3`; do # Wrong! some command $ i # Wrong! done
ls *.mp3
substitution is word breaking. Suppose we have the file 01 - Don't Eat the Yellow Snow.mp3
in the current directory 01 - Don't Eat the Yellow Snow.mp3
. The for
loop will go through each word from the file name and $i
will take the values: "01"
, "-"
, "Don't"
, "Eat"
, "the"
, "Yellow"
, "Snow.mp3"
.for i in "` ls * .mp3` "; do # Wrong! ...
i
will take the value, which is the concatenation of all file names separated by spaces.ls
completely unnecessary: ​​it is an external command that is simply not needed in this case. How, then, is it right? That's how:for i in * .mp3; do # Much better, but ... some command "$ i" # ... see trick number 2 done
*.mp3
pattern will be considered as one word, and the loop will go through each file name once.$file
and $target
do not contain spaces or wildcard characters.cp "$ file" "$ target"
cp 01 - Don't Eat the Yellow Snow.mp3 /mnt/usb
, and you will get a lot of errors like cp: cannot stat `01': No such file or directory
. If the values ​​of the variables $file
or $target
contain the characters *,?, [..] or (..) used in wildcard naming patterns, then in the case of the existence of files that match the pattern, the values ​​of the variables will be converted in the names of these files. Double quotes solve this problem, unless "$file"
starts with a dash -
in this case, cp
thinks that you are trying to give it another command line option.--
) between the cp
command and its arguments. A double hyphen tells cp
to stop searching for options:cp - "$ file" "$ target"
--
option. In this case, read on../
for the current one). For example:for i in ./*.mp3; do cp "$ i" / target ...
./-foo.mp3
, which is absolutely safe to use with cp
.[
does not exist or is empty, the string[$ foo = "bar"]
[= "bar"]
[
will be shocked by this syntax)[
:[multiple words here = "bar"]
["$ foo" = bar] # is near!
-
.[[
, which includes and significantly extends the old test
command (also known as [
)[[$ foo = bar]] # correct!
[[
and ]]
no longer need to quote variable names, since variables are no longer broken down into words and even empty variables are handled correctly. On the other hand, even if once again to put them in quotes, it does not hurt anything.[x "$ foo" = xbar] # is correct too!
x"$foo"
is required in code that should work in shells that do not support [[
, because if $foo
starts with -
, the command [
will be disoriented.[bar = "$ foo"] # that's right too!
[
does not care that the expression to the right of the "=" sign begins with -
. It simply uses this expression as a string. Only the left side requires such close attention.cd "` dirname "$ f" `"
`
) as a level of nesting, and the quotes inside it are separated from the outside.$()
syntax:cd "$ (dirname" $ ​​f ")"
$()
are grouped.&&
inside the “old” test
command or its equivalent [
. The bash parser sees &&
outside the brackets and splits your command into two, before and after &&
. Better use one of the options:[bar = "$ foo" -a foo = "$ bar"] # That's right! [bar = "$ foo"] && [foo = "$ bar"] # That's right too! [[$ foo = bar && $ bar = foo]] # That's right too!
[
- for the reasons discussed in the previous paragraph.||
. Use [[
, or -o
, or two commands [
.>
operator is used inside [[ ]]
, it is treated as a string comparison operator, not a number. In some cases, this may or may not work (and this will happen just when you least expect it). If >
is inside [ ]
, it is still worse: in this case, it is a redirection of the output from the file descriptor with the specified number. An empty file with the name 7
appears in the current directory, and the test
command completes successfully, unless the $foo
variable is empty.[ .. ]
or [[ .. ]]
.(( ))
:((foo> 7)) # That's right!
[$ foo -gt 7] # That's right too!
test ... -gt ...
will give an error if at least one of its arguments is not an integer. Therefore, it does not matter if the quotes are correctly placed: if the variable is empty, or contains spaces, or its value is not an integer, an error will occur in any case. Just carefully check the value of a variable before using it in the test
command.[[$ foo -gt 7]] # That's right too!
$count
variable will remain unchanged after exiting the loop, much to the surprise of the bash developer. Why it happens?for
loop is part of the pipeline and runs in a separate subshell with its copy of the variable $count
, and the unified value of the variable $count
from the parent shell: "0". When the loop ends, the copy of $count
used in the loop is discarded and the echo
command shows the unchanged initial value of $count
(“0”).# POSIX compatible count = 0 cat / etc / passwd | ( while read line; do count = $ ((count + 1)) done echo "total number of lines: $ count" )
# bash only! count = 0 while read line; do count = $ (($ count + 1)) done </ etc / passwd echo "total number of lines: $ count"
while read LINE; do echo "-> $ LINE" done <<(grep PATH / etc / profile)
if
and newbies often get the false impression that [
is part of a conditional syntax, just like brackets in conditional C language constructs.[
) is not part of the syntax, but a command that is equivalent to the test
command, except that the last argument of this command must be a closing bracket ]
.if
syntaxif COMMANDS then COMMANDS elif COMMANDS # optional then COMMANDS else # optional COMMANDS fi
[
or [[
![
is a command that accepts arguments and issues a return code; like all normal commands, it can display error messages, but, as a rule, it does not produce anything in STDOUT.if
executes the first set of commands, and depending on the return code of the last command from this set, determines whether a block of commands from the “then” section is executed or the script will continue.grep
, you do not need to enclose it in round, square or curly braces, backward quotes, or any other syntax element. Just write grep
as a command after the if
:if grep foo myfile> / dev / null; then ... fi
grep
: we don’t need a search result, we just want to know if the line is in the file. If grep
finds a string, it returns 0, and the condition is met; otherwise (no line in the file), grep
returns a value other than 0. In GNU grep, the redirection >/dev/null
can be replaced with the -q
option, which tells grep
'u that you don't need to output anything.[
is a command. As with any other command, bash assumes that the command is followed by a space, then the first argument, then a space, and so on. Therefore, you can not write anything without spaces! Right like this:if [bar = "$ foo"]
bar
, =
, "$foo"
(after substitution, but without word division) and ]
are arguments to the command [
, so there must be a space between each pair of arguments so that the shell can determine where which argument starts and ends.[
Is a command, not a syntax element between an if
and a condition, and certainly not a means of grouping. You cannot take the C syntax and convert it to the bash syntax by simply replacing the round brackets with square brackets.if [a = b] && [c = d]
if test a = b && test c = d
false
(any non-zero number), the condition body is skipped. If it returns true
, the second condition is satisfied; if it returns true
, the condition body is executed.Source: https://habr.com/ru/post/47706/
All Articles