Non ho una citazione concreta per il perché questo comportamento esiste, ma uscendo dalle note in SC2257* ci sono alcuni punti interessanti da notare nel manuale.
Quando un semplice comando diverso da una funzione incorporata o di shell deve essere eseguito, viene richiamato in un ambiente di esecuzione separato
§3.7.3 Ambiente di esecuzione dei comandi
Ciò riflette quanto notato da SC2257, anche se non è chiaro in quale ambiente venga valutato il valore del reindirizzamento. Tuttavia §3.1.1 Shell Operation sembra dire che il reindirizzamento avviene prima questo (sotto)ambiente di esecuzione viene richiamato:
Fondamentalmente, la shell fa quanto segue:
...
- Esegue le varie espansioni della shell....
- Esegue tutti i reindirizzamenti necessari e rimuove gli operatori di reindirizzamento e i relativi operandi dall'elenco degli argomenti.
- Esegue il comando.
Possiamo vedere che questo non è limitato alle espansioni aritmetiche ma anche ad altre espansioni che cambiano lo stato come :=
:
$ bash -c 'date >"${word:=wow}.txt"; echo "word=${word}"'
word=
$ bash -c 'echo >"${word:=wow}.txt"; echo "word=${word}"'
word=wow
È interessante notare che questo non sembra essere un ambiente subshell (ben definito), perché BASH_SUBSHELL
rimane impostato su 0
:
$ date >"${word:=$BASH_SUBSHELL}.txt"; ls
0.txt
Possiamo anche controllare altre shell e vedere che zsh
ha lo stesso comportamento, sebbene dash
non:
$ 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
Ho scremato il zsh
guide ma non ho trovato neanche una menzione esatta di questo comportamento.
Inutile dirlo , questo non sembra essere un comportamento ben documentato, quindi è una fortuna che ShellCheck possa aiutarci a rilevarlo. Tuttavia sembra essere un comportamento di vecchia data, è riproducibile in Bash 3, 4 e 5.
* Sfortunatamente il commit che ha aggiunto SC2257 non si collega a un problema o a qualsiasi altro ulteriore contesto.
Il consiglio di Shellcheck è valido; a volte i reindirizzamenti vengono eseguiti in subshell. Tuttavia, il punto cruciale di questo comportamento è quando si verificano espansioni:
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
Quando viene chiamato execute_disk_command(), esegue il fork e quindi esegue date(1). Dopo fork() e prima di execve(), vengono eseguiti i reindirizzamenti e le espansioni aggiuntive (tramite do_redirections()). Le variabili espanse e vincolate post-fork non si rifletteranno nella shell madre.
Dal punto di vista di BASH, tuttavia, questo è solo un semplice comando piuttosto che un comando subshell. Questa è una subshell implicita.
Vedere execute_disk_command() in 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.
(riferimenti presi da commit 9e49d343e3cd7e20dad1b86ebfb764e8027596a7 [browse tree])