36 votes

Besoin d'explications sur le comportement de la commande exec intégrée à Linux bash

À partir de ce lien j'ai le sujet de exec bash builtin commande:

Si la commande est fournie, elle remplace la coque sans la création d'un nouveau processus.

Maintenant j'ai le texte suivant bash script:


#!/bin/bash
exec ls;
echo 123;
exit 0

Cette exécuté, j'ai obtenu ceci:

cleanup.sh ex1.bash file.bash file.bash~ output.log //les fichiers à partir du répertoire courant

Maintenant, si j'ai ce script:


#!/bin/bash
exec ls | cat
echo 123

exit 0

J'obtiens le résultat suivant:


cleanup.sh
ex1.bash
file.bash
file.bash~
output.log
123

Ma question est:

Si lors de l' exec est invoquée , elle remplace la coque sans la création d'un nouveau processus, pourquoi, lorsqu'il est mis | cat, l' echo 123 est imprimé, mais sans elle, il ne l'est pas. Donc, je serais heureux si quelqu'un peut expliquer ce qu'est la logique de ce comportement.

Merci.

EDIT: Après @torek réponse, je reçois encore plus de mal à expliquer le comportement:

1.exec ls>out commande crée l' out le fichier et mettez le tout au lss'commande de résultat;

2.exec ls>out1 ls>out2 crée uniquement les fichiers, mais ne pas mettre à l'intérieur de n'importe quel résultat. Si la commande fonctionne comme suggéré, je pense que le numéro de commande 2 devrait avoir le même résultat que le numéro de commande 1 (encore plus, je pense qu'il ne devrait pas avoir créé l' out2 le fichier).

39voto

torek Points 25463

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 echos 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X