J'ai écrit un script qui passe des utilisateurs lors de l' exécution, et exécuté à l'aide de la redirection de fichier dans la norme. Donc , user-switch.sh
est ...
#!/bin/bash
whoami
sudo su -l root
whoami
Et l'exécuter avec bash
me donne le comportement que j'attends
$ bash < user-switch.sh
vagrant
root
Cependant, si j'exécute le script avec sh
, j'obtiens une sortie différente
$ sh < user-switch.sh
vagrant
vagrant
Pourquoi bash < user-switch.sh
donner une sortie différente de celle sh < user-switch.sh
?
Remarques:
- se produit sur deux boîtes différentes exécutant Debian Jessie
bash
io-redirection
stdin
dash
popedotninja
la source
la source
sudo su
: unix.stackexchange.com/questions/218169/…/bin/sh
sur (ma copie de) Debian est Dash, oui. Je ne savais même pas ce que c'était jusqu'à maintenant. Je suis resté avec bash jusqu'à présent.user-switch.sh
à l' origine dans un travail Jenkins et le script a été exécuté. L'exécution du même script en dehors de Jenkins a produit des résultats différents, et j'ai eu recours à la redirection de fichiers pour obtenir le comportement que j'ai vu dans Jenkins.Réponses:
Un script similaire, sans
sudo
, mais des résultats similaires:Avec
bash
, le reste du script va en entrée àsed
, avecdash
, le shell l'interprète.En cours
strace
d' exécution sur:dash
lit un bloc du script (huit Ko ici, plus que suffisant pour contenir l'intégralité du script), puis apparaîtsed
:Ce qui signifie que le descripteur de fichier est à la fin du fichier et
sed
ne verra aucune entrée. La partie restante étant tamponnée à l'intérieurdash
. (Si le script était plus long que la taille de bloc de 8 Ko, la partie restante serait lue parsed
.)Bash, par contre, cherche à revenir à la fin de la dernière commande:
Si l'entrée provient d'un tuyau, comme ici:
le rembobinage ne peut pas être fait, car les tuyaux et les prises ne sont pas recherchés. Dans ce cas, Bash revient à lire l'entrée un caractère à la fois pour éviter les surcharges. (
fd_to_buffered_stream()
ininput.c
) Faire un appel système complet pour chaque octet n'est pas très efficace en principe. Dans la pratique, je ne pense pas que les lectures seront un gros frais généraux par exemple par rapport au fait que la plupart des choses que le shell implique impliquent de nouveaux processus entiers.Une situation similaire est la suivante:
Le sous-shell doit s'assurer de
read
ne lire que la première nouvelle ligne, de sorte quehead
la ligne suivante soit visible. (Cela fonctionnedash
aussi avec .)En d'autres termes, Bash va à des longueurs supplémentaires pour prendre en charge la lecture de la même source pour le script lui-même et pour les commandes exécutées à partir de celui-ci.
dash
non. Lezsh
, etksh93
empaqueté dans Debian vont avec Bash à ce sujet.la source
strace
plus tard et comparerai les sorties. Je n'avais pas pensé à utiliser cette approche.Le shell lit le script à partir de l'entrée standard. Dans le script, vous exécutez une commande qui veut également lire l'entrée standard. Quelle entrée va aller où? Vous ne pouvez pas le dire de manière fiable .
La façon dont les shells fonctionnent est qu'ils lisent un morceau de code source, l'analysent, et s'ils trouvent une commande complète, exécutez la commande, puis passez au reste du morceau et au reste du fichier. Si le morceau ne contient pas de commande complète (avec un caractère de fin à la fin - je pense que tous les shells sont lus jusqu'à la fin d'une ligne), le shell lit un autre morceau, et ainsi de suite.
Si une commande du script essaie de lire à partir du même descripteur de fichier que le shell lit le script, alors la commande trouvera tout ce qui vient après le dernier morceau lu. Cet emplacement est imprévisible: il dépend de la taille de bloc choisie par le shell, et cela peut dépendre non seulement du shell et de sa version mais de la configuration de la machine, de la mémoire disponible, etc.
Bash cherche à la fin du code source d'une commande dans le script avant d'exécuter la commande. Ce n'est pas quelque chose sur lequel vous pouvez compter, non seulement parce que d'autres shells ne le font pas, mais aussi parce que cela ne fonctionne que si le shell lit à partir d'un fichier normal. Si le shell lit un tube (par exemple
ssh remote-host.example.com <local-script-file.sh
), les données lues sont lues et ne peuvent pas être non lues.Si vous souhaitez transmettre une entrée à une commande dans le script, vous devez le faire explicitement, généralement avec un document ici . (Un document ici est généralement le plus pratique pour une entrée multiligne, mais n'importe quelle méthode fera l'affaire.) Le code que vous avez écrit ne fonctionne que dans quelques shells, uniquement si le script est passé en entrée au shell à partir d'un fichier normal; si vous vous attendiez à ce que le second
whoami
soit passé en entrée àsudo …
, détrompez-vous, en gardant à l'esprit que la plupart du temps le script n'est pas passé à l'entrée standard du shell.Notez que cette décennie, vous pouvez utiliser
sudo -i root
. La coursesudo su
est un hack du passé.la source