Dans ce cas particulier, vous avez l' exec
dans un pipeline. Afin d'exécuter une série de pipeline commandes du shell d'abord à la fourchette, faire un sous-shell. (Plus précisément, il a pour créer la pipe, puis à la fourche, de sorte que tout marche "à gauche" de la pipe peut avoir sa sortie est envoyée à tout ce qui est "à droite" de la pipe.)
De voir que c'est en fait ce qui se passe, de comparer:
{ ls; echo this too; } | cat
avec:
{ exec ls; echo this too; } | cat
L'ancien s'exécute ls
sans quitter le sous-shell, de sorte que ce sous-shell est donc toujours là pour exécuter l' echo
. Ce dernier s'exécute ls
en laissant le sous-shell, qui n'est donc plus là pour faire l' echo
, et this too
n'est pas imprimé.
(L'utilisation de curly-accolades { cmd1; cmd2; }
normalement supprime le sous-shell fourche action que vous obtenez avec des parenthèses (cmd1; cmd2)
, mais dans le cas d'un tuyau, la fourche est "forcé", comme elle l'avait fait.)
La Redirection du shell courant se produit uniquement si il n'y a "rien de courir", comme c'était le cas, après le mot exec
. Ainsi, par exemple, exec >stdout 4<input 5>>append
modifie le shell en cours, mais exec foo >stdout 4<input 5>>append
tente de commande exec foo
. [Note: ce n'est pas strictement exacte; voir annexe.]
Il est intéressant de noter, dans un shell interactif, après l' exec foo >output
échoue car il n'y a pas de commande foo
, la coquille autour de bâtons, mais stdout reste redirigé vers le fichier output
. (Vous pouvez récupérer avec exec >/dev/tty
. Dans un script, l'échec de l' exec foo
se termine le script.)
Avec un coup de chapeau à @Pumbaa80, voici quelque chose d'encore plus parlante:
#! /bin/bash
shopt -s execfail
exec ls | cat -E
echo this goes to stdout
echo this goes to stderr 1>&2
(remarque: cat -E
est simplifiée en bas de mon habituel cat -vET
, ce qui est de ma pratique de go-to pour "laissez-moi voir les caractères non imprimables dans une manière reconnaissable"). Lorsque ce script est exécuté, la sortie de ls
a cat -E
appliqué (sur Linux, cela rend la fin de la ligne visible comme un signe de$), mais la sortie est envoyée sur la sortie standard stdout et stderr (sur les deux autres lignes) n'est pas redirigé. Modifier l' | cat -E
de > out
et, après l'exécution du script, observer le contenu du fichier out
: les deux derniers echo
s ne sont pas là.
Maintenant changer l' ls
de foo
(ou d'une autre commande qui ne sera pas trouvé) et exécutez à nouveau le script. Cette fois, la sortie est:
$ ./demo.sh
./demo.sh: line 3: exec: foo: not found
this goes to stderr
et le fichier out
a maintenant le contenu produit par le premier echo
ligne de.
Cela rend ce exec
"vraiment" comme une évidence que possible (mais pas plus évident, comme Albert Einstein n'a pas la mettre :-) ).
Normalement, lorsque le shell va à exécuter un "simple commande" (voir la page de manuel pour la définition précise, mais cela exclut spécifiquement les commandes dans un "pipeline"), il prépare tout redirection d'e/S les opérations spécifiées avec <
, >
, et ainsi de suite en ouvrant les fichiers nécessaires. Puis la coquille invoque fork
(ou l'équivalent mais plus économes en variante, comme vfork
ou clone
selon l'OS sous-jacent, la configuration, etc), et, dans le processus de l'enfant, réorganise les descripteurs de fichiers ouverts (à l'aide d' dup2
des appels ou équivalent) pour atteindre la finale désirée arrangements: > out
déplace le descripteur ouvert à fd 1-sortie standard (stdout)-alors que 6> out
déplace le descripteur ouvert à fd 6.
Si vous spécifiez l' exec
mot-clé, cependant, le shell supprime l' fork
étape. Il fait tout le ouverture de dossier et de fichier de descripteur de-réorganiser comme d'habitude, mais cette fois, elle touche à tout et toutes les commandes ultérieures. Enfin, après avoir fait toutes les redirections, le shell tente d' execve()
(dans l'appel-système de sens) de la commande, si il y en a un. Si il n'y a pas de commande, ou si l' execve()
d'échec de l'appel et la coquille est censé continuer à exécuter (interactif ou vous avez défini execfail
), le shell soldats. Si l' execve()
réussit, le shell n'existe plus, ayant été remplacé par la nouvelle commande. Si execfail
n'est pas définie et la coque n'est pas interactif, le shell sorties.
(Il y a aussi la complication supplémentaire de l' command_not_found_handle
fonction shell: bash de l' exec
semble supprimer l'exécution, sur la base des résultats du test. L' exec
mot-clé fait en général le shell de ne pas regarder ses propres fonctions, c'est à dire, si vous avez une coquille de la fonction f, l'exécution d' f
comme une simple commande exécute la fonction shell, comme n' (f)
qui s'exécute dans un sous-shell, mais l'exécution (exec f)
saute.)
Quant à savoir pourquoi ls>out1 ls>out2
crée deux fichiers (avec ou sans exec
), c'est assez simple: la coquille s'ouvre à chaque redirection, puis utilise dup2
de déplacer les descripteurs de fichier. Si vous avez deux ordinaire, >
redirections, la coquille s'ouvre à la fois, se déplace le premier à fd 1 (stdout), puis passe la seconde pour fd 1 (stdout nouveau), en clôture de la première dans le processus. Enfin, il exécute ls ls
, parce que c'est ce qu'il en reste après le retrait de l' >out1 >out2
. Tant qu'il n'existe pas de fichier nommé ls
, l' ls
commande se plaint de stderr, et n'écrit rien sur la sortie standard stdout.