Je n'ai pas de citation concrète pour pourquoi ce comportement existe, mais en partant des notes dans SC2257*, il y a quelques points intéressants à noter dans le manuel.
Lorsqu'une simple commande autre qu'une fonction intégrée ou shell doit être exécuté, il est appelé dans un environnement d'exécution séparé
§3.7.3 Environnement d'exécution des commandes
Cela reflète ce que SC2257 note, bien qu'il ne soit pas clair dans quel environnement la valeur de redirection est évaluée. Cependant, §3.1.1 Shell Operation semble dire que la redirection se produit avant ce (sous)environnement d'exécution est appelé :
Fondamentalement, le shell effectue les opérations suivantes :
...
- Effectue les différentes extensions du shell....
- Effectue toutes les redirections nécessaires et supprime les opérateurs de redirection et leurs opérandes de la liste d'arguments.
- Exécute la commande.
Nous pouvons voir que cela ne se limite pas aux extensions arithmétiques mais aussi à d'autres extensions à changement d'état comme :=
:
$ bash -c 'date >"${word:=wow}.txt"; echo "word=${word}"'
word=
$ bash -c 'echo >"${word:=wow}.txt"; echo "word=${word}"'
word=wow
Fait intéressant, cela ne semble pas être un environnement de sous-shell (bien défini), car BASH_SUBSHELL
reste réglé sur 0
:
$ date >"${word:=$BASH_SUBSHELL}.txt"; ls
0.txt
Nous pouvons également vérifier d'autres shells et voir que zsh
a le même comportement, bien que 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
J'ai survolé le zsh
guide, mais je n'y ai pas non plus trouvé de mention exacte de ce comportement.
Inutile de dire , cela ne semble pas être un comportement bien documenté, il est donc heureux que ShellCheck puisse aider à le détecter. Cela semble cependant être un comportement de longue date, il est reproductible dans Bash 3, 4 et 5.
* Malheureusement, le commit qui a ajouté SC2257 n'est pas lié à un problème ou à tout autre contexte supplémentaire.
Les conseils de Shellcheck sont judicieux ; parfois les redirections sont effectuées dans des sous-shells. Cependant, le nœud de ce comportement est lorsque des expansions se produisent :
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
Quand execute_disk_command() est appelé, il bifurque puis exécute date(1). Après le fork() et avant le execve(), les redirections et les extensions supplémentaires sont effectuées (via do_redirections()). Les variables développées et liées post-fork ne seront pas reflétées dans le shell parent.
Du point de vue de BASH, cependant, il ne s'agit que d'une simple commande plutôt que d'une commande de sous-shell. Ceci est un sous-shell implicite.
Voir execute_disk_command() dans 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.
(références extraites du commit 9e49d343e3cd7e20dad1b86ebfb764e8027596a7 [parcourir l'arborescence])