script bash: résultats différents en cas d'appel avec ou sans sudo

10

Dans Ubuntu 16.04.3, j'ai un script bash très simple:

test.sh

[[ 0 == 0 ]] && result="true" || result="false"
echo $result
echo $USER $SHELL $0

Lorsque je l'appelle en tant qu'utilisateur non root meou en tant que root, cela fonctionne comme prévu. Si j'utilise sudo ./test.sh, il se plaint d'une erreur de syntaxe:

$ ./test.sh
true
me /bin/bash ./test.sh

$ sudo su
# ./test.sh 
true
root /bin/bash ./test.sh

# exit
$ sudo ./test.sh
./test.sh: 1: ./test.sh: [[: not found
false
root /bin/bash ./test.sh

Qu'est-ce qui peut causer cela? Comment puis-je le corriger pour mepouvoir utiliser ce script à la fois normalement et avec sudo?

James Newton
la source
3
Conseil de pro: inutile de courirsudo su . Exécutez simplement sudo -iou à la sudo -splace.
terdon
@terdon sudo -ichange l'emplacement en /root. sudo suou sudo -sne changez pas l'emplacement du répertoire.
James Newton
Oui, lisez la question à laquelle j'ai lié avant pour savoir pourquoi. Et désolé, j'ai édité mon commentaire précédent, j'avais oublié de le mentionner -s.
terdon

Réponses:

20

Chaque script commence par un Shebang , sans lui le shell qui démarre votre script ne sait pas à quel interprète doit exécuter votre script 1 et pourrait - comme dans le cas sudo ./script.shici - l'exécuter avec shlequel, dans Ubuntu 16.04 est lié dash. L' expression conditionnelle [[ est une bashcommande composée , dashelle ne sait donc pas comment la gérer et renvoie l'erreur que vous avez rencontrée.

La solution ici est d'ajouter

#!/bin/bash

comme première ligne de votre script. Vous pouvez obtenir le même résultat lorsque vous l'appelez explicitement avec sudo bash ./script.sh, mais un shebang est le chemin à parcourir.
Pour vérifier quel shell exécute votre script, ajoutez- echo $0y. Ce n'est pas la même chose echo $SHELL , citant wiki.archlinux.org :

SHELL contient le chemin d'accès au shell préféré de l'utilisateur. Notez que ce n'est pas nécessairement le shell en cours d'exécution, bien que Bash définisse cette variable au démarrage.

1: Comme vous avez commencé ./test.shavec bashcela bash, il en va de même pour le sudo susous - shell.

dessert
la source
1
Notez également que bash exécute un script sans shebang en utilisant bash, non /bin/sh.
muru
@dessert Cela le corrige. Merci! Comment puis-je vérifier à partir d'un script quel shell l'exécute? ( echo $0Me donne le nom du script: ./test.sh)
James Newton
@JamesNewton aucun moyen portable, AFAIK, mais vous pouvez vérifier ce qui /proc/$$/exepointe vers. Aussi , vous pouvez tester différentes variables comme $BASH_VERSION, $ZSH_VERSION, etc. (mais tiret ne précise pas une telle variable)
muru
5

Comme l'a expliqué @dessert , le problème ici est que votre script n'a pas de ligne shebang . Sans shebang, sudotentera par défaut d'exécuter le fichier à l'aide de /bin/sh. Je ne l'ai trouvé nulle part, mais j'ai confirmé en vérifiant le sudocode source où j'ai trouvé ce qui suit dans le fichier pathnames.h:

#ifndef _PATH_BSHELL
#define _PATH_BSHELL "/bin/sh"
#endif /* _PATH_BSHELL */

Cela signifie "définir si la variable _PATH_BSHELLn'est pas définie, définissez-la sur /bin/sh". Ensuite, dans le configurescript inclus dans l'archive tar source, nous avons:

for p in "/bin/bash" "/usr/bin/sh" "/sbin/sh" "/usr/sbin/sh" "/bin/ksh" "/usr/bin/ksh" "/bin/bash" "/usr/bin/bash"; do
    if test -f "$p"; then
    found=yes
    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $p" >&5
$as_echo "$p" >&6; }
    cat >>confdefs.h <<EOF
#define _PATH_BSHELL "$p"
EOF

    break
    fi
done

Cette boucle cherchera /bin/bash, /usr/bin/sh, /sbin/sh, /usr/sbin/shou /bin/kshet définit ensuite la _PATH_BSHELLà celui qui a été trouvé en premier . Étant donné qu'il /bin/shétait le premier de la liste et qu'il existe, _PATH_BSHELLest défini sur /bin/sh. Le résultat de tout cela est que le shell par défaut de sudosauf indication contraire est /bin/sh.

Donc, sudopar défaut, les choses seront exécutées en utilisant /bin/shet, sur Ubuntu, c'est un lien symbolique vers dashun shell compatible POSIX minimal:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Feb 27  2015 /bin/sh -> dash

La [[construction est une fonctionnalité bash, elle n'est pas définie par la norme POSIX et n'est pas comprise par dash:

$ bash -c '[[ true ]] && echo yes'
yes
$ dash -c '[[ true ]] && echo yes'
dash: 1: [[: not found

En détail, dans les trois invocations que vous avez essayées:

  1. ./test.sh

    Non sudo; en l'absence d'une ligne de shebang, votre shell tentera d'exécuter le fichier lui-même. Puisque vous exécutez bash, cela fonctionnera bash ./test.shet fonctionnera efficacement .

  2. sudo susuivi de ./test.sh.

    Ici, vous lancez un nouveau shell pour l'utilisateur root. Ce sera le shell défini dans la $SHELLvariable d'environnement pour cet utilisateur et, sur Ubuntu, le shell par défaut de root est bash:

    $ grep root /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    
  3. sudo ./test.sh

    Ici, vous laissez sudoexécuter directement la commande. Étant donné que son shell par défaut est /bin/shcomme expliqué ci-dessus, cela lui fait exécuter le script /bin/sh, ce qui est dashet il échoue car dashne comprend pas [[.


Remarque : les détails de la sudodéfinition du shell par défaut semblent être un peu plus complexes. J'ai essayé de changer les fichiers mentionnés dans ma réponse pour pointer vers, /bin/bashmais j'étais sudotoujours en défaut /bin/sh. Il doit donc y avoir d'autres endroits dans le code source où le shell par défaut est défini. Néanmoins, le point principal (par sudodéfaut sh) est toujours d'actualité.

terdon
la source
Je ne pouvais pas le trouver documenté nulle part - moi non plus, je supposais simplement qu'il utiliserait /bin/shle message d'erreur - quoi d'autre pourrait-il être possible? La question est admirablement répondue dans Quel shell sudo utilise · SO , voir aussi man sudo, section EXÉCUTION DE COMMANDE . Il s'avère que sudon'utilise pas un shell intermédiaire !
dessert
1
@dessert oui, il utilise sa propre implémentation de l' execveappel système qui est par défaut sh. Et non, les shells intermédiaires ne sont pas pertinents, il ne s'agit pas du shell qui exécute la commande mais de l'interpréteur de shell utilisé pour lire le script shell donné. Donc non, il ne lance pas de shell intermédiaire mais il a toujours besoin d'un interpréteur de shell pour les scripts shell.
terdon