Nemám konkrétní citaci, proč toto chování existuje, ale když vyjdeme z poznámek v SC2257*, v manuálu je třeba poznamenat několik zajímavých bodů.
Když jednoduchý příkaz jiný než vestavěná nebo shellová funkce má být spuštěn, je vyvolán v samostatném prováděcím prostředí
§3.7.3 Prostředí provádění příkazů
To odráží to, co poznamenává SC2257, i když není jasné, ve kterém prostředí je hodnota přesměrování vyhodnocena. Zdá se však, že §3.1.1 Shell Operation říká, že k přesměrování dojde před toto prováděcí (pod)prostředí je vyvoláno:
V podstatě shell dělá následující:
...
- Provádí různé expanze shellu....
- Provede všechna nezbytná přesměrování a odstraní operátory přesměrování a jejich operandy ze seznamu argumentů.
- Provede příkaz.
Vidíme, že to není omezeno na aritmetické expanze, ale také další expanze měnící stav, jako je :=
:
$ bash -c 'date >"${word:=wow}.txt"; echo "word=${word}"'
word=
$ bash -c 'echo >"${word:=wow}.txt"; echo "word=${word}"'
word=wow
Zajímavé je, že to nevypadá jako (dobře definované) prostředí subshell, protože BASH_SUBSHELL
zůstane nastavena na 0
:
$ date >"${word:=$BASH_SUBSHELL}.txt"; ls
0.txt
Můžeme také zkontrolovat některé další shelly a uvidíme, že zsh
má stejné chování, ačkoli dash
ne:
$ zsh -c 'date >"${word:=wow}.txt"; echo "word=${word}"'
word=
$ zsh -c 'echo >"${word:=wow}.txt"; echo "word=${word}"'
word=wow
$ dash -c 'date >"${word:=wow}.txt"; echo "word=${word}"'
word=wow
$ dash -c 'echo >"${word:=wow}.txt"; echo "word=${word}"'
word=wow
Přečetl jsem zsh
průvodce, ale ani tam jsme nenašli přesnou zmínku o tomto chování.
Netřeba říkat , nezdá se, že by to bylo dobře zdokumentované chování, takže je štěstí, že ShellCheck může pomoci to zachytit. Zdá se však, že jde o dlouhodobé chování, je reprodukovatelné v Bash 3, 4 a 5.
* Potvrzení, které přidalo SC2257, bohužel neodkazuje na problém ani žádný další kontext.
Rada Shellchecku je správná; někdy se přesměrování provádějí v subshellech. Jádrem tohoto chování je však kdy dochází k expanzím:
bind_int_variable variables.c:3410 cnt = 2, late binding
expr_bind_variable expr.c:336
exp0 expr.c:1040
exp1 expr.c:1007
exppower expr.c:962
expmuldiv expr.c:887
exp3 expr.c:861
expshift expr.c:837
exp4 expr.c:807
exp5 expr.c:785
expband expr.c:767
expbxor expr.c:748
expbor expr.c:729
expland expr.c:702
explor expr.c:674
expcond expr.c:627
expassign expr.c:512
expcomma expr.c:492
subexpr expr.c:474
evalexp expr.c:439
param_expand subst.c:9498 parameter expansion, including arith subst
expand_word_internal subst.c:9990
shell_expand_word_list subst.c:11335
expand_word_list_internal subst.c:11459
expand_words_no_vars subst.c:10988
redirection_expand redir.c:287 expansions post-fork()
do_redirection_internal redir.c:844
do_redirections redir.c:230 redirections are done in child process
execute_disk_command execute_cmd.c:5418 fork to run date(1)
execute_simple_command execute_cmd.c:4547
execute_command_internal execute_cmd.c:842
execute_command execute_cmd.c:394
reader_loop eval.c:175
main shell.c:805
Když se zavolá příkaz execute_disk_command(), rozdělí se a poté provede date(1). Po fork() a před execve() se provedou přesměrování a další rozšíření (prostřednictvím do_redirections()). Proměnné expandované a vázané po rozvětvení se neodrazí v nadřazeném shellu.
Z pohledu BASH je to však spíše jednoduchý příkaz než příkaz subshell. Toto je implicitní podshell.
Viz execute_disk_command() v execute_cmd.c
Execute a simple command that is hopefully defined in a disk file
somewhere.
1) fork ()
2) connect pipes
3) look up the command
4) do redirections
5) execve ()
6) If the execve failed, see if the file has executable mode set.
If so, and it isn't a directory, then execute its contents as
a shell script.
(odkazy převzaty z potvrzení 9e49d343e3cd7e20dad1b86ebfb764e8027596a7 [procházet strom])