Reputation: 5062
I have the following script :
#!/bin/bash
# initialisation of the script
mkdir -p test_dir
touch test_dir/test{1..15}
touch test_dir/test{a..e}
# enabling etended glob
shopt -s extglob
# we count the number of files which name is touchNUMBER
for f in test_dir/test+([0-9]); do ((count++)); done; echo $count
It works just fine and prints 15
.
However, when I try to concatenate this script to a one-liner, it returns an error :
#!/bin/bash
mkdir -p test_dir
touch test_dir/test{1..15}
touch test_dir/test{a..e}
shopt -s extglob; for f in test_dir/test+([0-9]); do ((count++)); done; echo $count
Output :
./test.sh: line 7: syntax error near unexpected token `('
It seems bash
doesn't evaluate the shopt -s extglob
before determining the correctness of the syntax of this line.
EDIT:
Interestingly enough, replacing the incriminated line with :
shopt -s extglob; sleep 10;for f in test_dir/test+([0-9]); do ((count++)); done; echo $count
Displays the same error message instantly, thus confirming the error message is raised before the execution of the line.
Why is that ? Is there a way around ?
Upvotes: 1
Views: 57
Reputation:
Bash reads scripts or input line by line. Then it parses the whole line dividing it into tokens using metacharacters (|
&
;
(
)
<
>
space
tab
newline
) while it also recognize quotes and expansions.
And, it is only the whole line has been characterized that each part starts being executed.
In the simple case, each command should be placed in its own separate line.
This works:
$ shopt -u extglob
$ shopt -s extglob
$ echo "test_dir/test"+([0-9])
test_dir/test01 test_dir/test11 test_dir/test22
While this does not work:
$ shopt -u extglob
$ shopt -s extglob ; echo "test_dir/test"+([0-9])
bash: syntax error near unexpected token `('
But that is not the whole history. If we manage to delay the evaluation with quotes or with an expansion, the line will work:
$ shopt -u extglob
$ shopt -s extglob ; echo $(echo "test_dir/test"+([0-9]))
test_dir/test01 test_dir/test11 test_dir/test22
Or:
$ shopt -u extglob
$ shopt -s extglob ; a=$(echo test_dir/test+([0-9])); echo "$a"
test_dir/test01 test_dir/test11 test_dir/test22
The reason is that a sub-shell $(…)
inherits the condition of the parent shell at the point of evaluation. The parent shell can not expand what will be inside a sub-shell until said sub-shell is actually started.
However, as correctly said by @chepner:
There is no reason to make this a one-liner in a script. One-liners are meant to reduce typing for frequently executed interactive commands; there is no need to do so in a script, where readability should be prioritized.
Upvotes: 0
Reputation: 530990
bash
processes the script line by line. In the first case, shopt -s extglob
has been executed by the time the for
loop is parsed. In the error case, you have a single line that after parsing will be recognized as two commands separated by ;
. However, this means shopt -x extglob
has not yet been executed when bash
needs to recognize the extended pattern +([0-9])
.
There is no reason to make this a one-liner in a script. One-liners are meant to reduce typing for frequently executed interactive commands; there is no need to do so in a script, where readability should be prioritized.
Upvotes: 4