J'essaie de comprendre comment Bash traite exactement la ligne suivante:
$(< "$FILE")
Selon la page de manuel de Bash, cela équivaut à:
$(cat "$FILE")
et je peux suivre le raisonnement de cette deuxième ligne. Bash effectue une expansion variable $FILE
, entre la substitution de commandes, transmet la valeur de $FILE
à cat
, cat sort le contenu de $FILE
la sortie standard, la substitution de commandes se termine en remplaçant la ligne entière par la sortie standard résultant de la commande à l'intérieur et Bash tente de l'exécuter comme une commande simple.
Cependant, pour la première ligne que j'ai mentionnée ci-dessus, je la comprends comme suit: Bash effectue la substitution de variables $FILE
, Bash s'ouvre $FILE
pour la lecture sur l'entrée standard, l'entrée standard est en quelque sorte copiée sur la sortie standard , la substitution de commandes se termine et Bash tente d'exécuter la norme résultante production.
Quelqu'un peut-il m'expliquer comment le contenu de $FILE
stdin passe de stdout?
la source
bash
interprètera cela commecat filename
", voulez-vous dire que ce comportement est spécifique à la substitution de commandes? Parce que si je cours tout< filename
seul, bash ne s'en sort pas. Il ne sortira rien et me renverra à une invite.cat < filename
lacat filename
que je m'y oppose et peut revenir.|
crée un tube entre deux sous-processus (ou, avec certains shells, d'un sous-processus vers l'entrée standard du shell). L'opérateur shell$(…)
crée un tube à partir d'un sous-processus vers le shell lui-même (et non vers son entrée standard). L'opérateur shell<
n'implique pas de canal, il ouvre uniquement un fichier et déplace le descripteur de fichier vers l'entrée standard.< file
n'est pas le même quecat < file
(saufzsh
là où c'est comme$READNULLCMD < file
).< file
est parfaitement POSIX et s'ouvre justefile
pour la lecture puis ne fait rien (ilfile
est donc tout de suite proche). C'est$(< file)
ou`< file`
c'est un opérateur spécial deksh
,zsh
etbash
(et le comportement n'est pas spécifié dans POSIX). Voir ma réponse pour plus de détails.$(cmd1) $(cmd2)
sera typiquement le même que$(cmd1; cmd2)
. Mais regardez le cas oùcmd2
est< file
. Si nous disons$(cmd1; < file)
, le fichier n'est pas lu, mais, avec$(cmd1) $(< file)
, il l'est. Il est donc incorrect de dire que$(< file)
c'est juste un cas ordinaire$(command)
avec une commande de< file
.$(< …)
est un cas particulier de substitution de commandes et non une utilisation normale de la redirection.$(<file)
(fonctionne également avec`<file`
) est un opérateur spécial du shell Korn copié parzsh
etbash
. Cela ressemble beaucoup à la substitution de commandes, mais ce n'est pas vraiment le cas.Dans les shells POSIX, une commande simple est:
Toutes les parties sont facultatives, vous pouvez avoir des redirections uniquement, des commandes uniquement, des affectations uniquement ou des combinaisons.
S'il y a des redirections mais pas de commande, les redirections sont effectuées (donc un
> file
s'ouvrirait et tronqueraitfile
), mais alors rien ne se passe. DoncOuvre
file
en lecture, mais rien ne se passe car il n'y a pas de commande. Donc, lefile
est fermé et c'est tout. S'il$(< file)
s'agissait d'une simple substitution de commande , elle se développerait à néant.Dans la spécification POSIX , dans
$(script)
, si sescript
compose uniquement de redirections, cela produit des résultats non spécifiés . C'est pour permettre ce comportement spécial du shell Korn.Dans ksh (ici testé avec
ksh93u+
), si le script se compose d'une et d'une seule commande simple (bien que les commentaires soient autorisés avant et après) qui ne se compose que de redirections (pas de commande, pas d'affectation) et si la première redirection est un stdin (fd 0) entrée uniquement (<
,<<
ou<<<
) redirection, donc:$(< file)
$(0< file)
$(<&3)
(également en$(0>&3)
fait car c'est en fait le même opérateur)$(< file > foo 2> $(whatever))
mais non:
$(> foo < file)
$(0<> file)
$(< file; sleep 1)
$(< file; < file2)
puis
<&3
) moins les caractères de fin de ligne.comme si,
$(cat < file)
sauf quecat
$(<${file=foo.txt})
ou$(<file$((++n)))
)Dans
zsh
, il est le même , sauf que ce comportement spécial est déclenché quand il ne n'y a qu'une seule redirection d'entrée de fichier (<file
ou0< file
, non<&3
,<<<here
,< a < b
...)Cependant, sauf lors de l'émulation d'autres shells, dans:
c'est quand il n'y a que des redirections d'entrée sans commandes, en dehors de la substitution de commandes,
zsh
exécute le$READNULLCMD
(un pager par défaut), et quand il y a des redirections d'entrée et de sortie, le$NULLCMD
(cat
par défaut), donc même s'il$(<&3)
n'est pas reconnu comme spécial , il fonctionnera toujours commeksh
si en appelant un pager pour le faire (ce pager agissant commecat
puisque sa sortie standard sera un pipe).Cependant , alors que
ksh
l »$(< a < b)
élargiraient au contenu dea
, danszsh
, il élargit le contenu dea
etb
(ou tout simplementb
si l'multios
option est désactivée),$(< a > b)
copiaienta
àb
et d' élargir à rien, etc.bash
a un opérateur similaire mais avec quelques différences:les commentaires sont autorisés avant mais pas après:
fonctionne mais:
se développe à rien.
comme dans
zsh
, un seul fichier redirection stdin, bien qu'il n'y ait pas de retour à a$READNULLCMD
, alors$(<&3)
,$(< a < b)
effectuez les redirections mais ne développez rien.bash
n'invoque pascat
, il bifurque toujours un processus qui alimente le contenu du fichier via un tube, ce qui en fait beaucoup moins d'optimisation que dans d'autres shells. C'est en effet comme un$(cat < file)
oùcat
serait un builtincat
.$(<${file=foo.txt})
, mentionné ci-dessus par exemple, cette$file
cession est perdue par la suite).Dans
bash
,IFS= read -rd '' var < file
(fonctionne également danszsh
) est un moyen plus efficace de lire le contenu d'un fichier texte dans une variable. Il présente également l'avantage de conserver les caractères de nouvelle ligne de fin. Voir aussi$mapfile[file]
danszsh
(dans lezsh/mapfile
module et uniquement pour les fichiers normaux) qui fonctionne également avec les fichiers binaires.Notez que les variantes basées sur pdksh
ksh
ont quelques variantes par rapport à ksh93. D'intérêt, dansmksh
(un de ces coquilles dérivées de pdksh), dansest optimisé en ce que le contenu du document ici (sans les caractères de fin) est développé sans qu'un fichier temporaire ou un canal ne soit utilisé comme c'est le cas autrement pour les documents ici, ce qui en fait une syntaxe de citation multi-lignes efficace.
Pour être portable sur toutes les versions de
ksh
,zsh
etbash
, le mieux est de se limiter à$(<file)
éviter les commentaires et en gardant à l'esprit que les modifications des variables apportées à l'intérieur peuvent ou non être conservées.la source
$(<)
s'agit d'un opérateur sur les noms de fichiers? Est<
dans$(<)
un opérateur de redirection, ou pas un opérateur seul, et doit faire partie de l'opérateur entier$(<)
?$(<file)
est destiné à s'étendre au contenu defile
la même manière que le$(cat < file)
ferait. La façon dont cela se fait varie d'un shell à l'autre, ce qui est décrit en détail dans la réponse. Si vous le souhaitez, vous pouvez dire que c'est un opérateur spécial qui est déclenché lorsque ce qui ressemble à une substitution de commande (syntaxiquement) contient ce qui ressemble à une redirection stdin unique (syntaxiquement), mais encore une fois avec des mises en garde et des variations en fonction du shell comme indiqué ici .n<&m
etn>&m
faire la même chose? Je ne le savais pas, mais je suppose que ce n'est pas trop surprenant.dup(m, n)
. Je peux voir des preuves que ksh86 utilise stdio et d'autresfdopen(fd, "r" or "w")
, donc cela aurait pu avoir de l'importance à l'époque. Mais utiliser stdio dans un shell n'a pas beaucoup de sens, donc je ne m'attends pas à ce que vous trouviez un shell moderne où cela fera une différence. Une différence est que>&n
c'estdup(n, 1)
(court pour1>&n
), tandis que<&n
estdup(n, 0)
(court pour0<&n
).dup2()
;dup()
ne prend qu'un seul argument et, commeopen()
, utilise le descripteur de fichier le plus bas disponible. (Aujourd'hui, j'ai appris qu'il y a unedup3()
fonction .)Parce
bash
que cela le fait en interne pour vous, développez le nom de fichier et transfère le fichier en sortie standard, comme si vous deviez le faire$(cat < filename)
. C'est une fonctionnalité bash, peut-être que vous devez regarder dans lebash
code source pour savoir exactement comment cela fonctionne.Voici la fonction pour gérer cette fonctionnalité (à partir
bash
du code source, du fichierbuiltins/evalstring.c
):Une note qui
$(<filename)
n'est pas exactement équivalente à$(cat filename)
; ce dernier échouera si le nom de fichier commence par un tiret-
.$(<filename)
était à l'origine deksh
, et a été ajouté àbash
deBash-2.02
.la source
cat filename
échouera si le nom de fichier commence par un tiret car cat accepte les options. Vous pouvez contourner cela sur la plupart des systèmes modernes aveccat -- filename
.Considérez la substitution de commandes comme l'exécution d'une commande comme d'habitude et le vidage de la sortie au point où vous exécutez la commande.
foo=$(echo "bar")
définira la valeur de la variable$foo
surbar
; la sortie de la commandeecho bar
.Substitution de commande
la source
$(< file)
, et il n'a pas besoin d'un tutoriel sur le cas général. Si vous dites que$(< file)
c'est juste un cas ordinaire$(command)
avec une commande de< file
, alors vous dites la même chose qu'Adam Katz et vous vous trompez tous les deux.