sudo がシェバン行なしでシェルスクリプトを実行するために使用するシェル

sudo がシェバン行なしでシェルスクリプトを実行するために使用するシェル

私の環境はUbuntu 12.04 LTSで、sudoバージョンは1.8.3p1です。

まず、通常のユーザーとしてログインします。

$ whoami
fin

$ cat /etc/passwd | grep -i "root\|fin"
root:x:0:0:root:/root:/bin/bash
fin:x:1000:1000:This is a normal user:/home/fin:/bin/bash

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Mar 30  2012 /bin/sh -> dash

$ ls -l /bin/bash
-rwxr-xr-x 1 root root 920788 Apr  3  2012 /bin/bash

$ echo $SHELL
/bin/bash

$ ps | grep "$$" | awk '{ print $4 }'
bash

$ ls -l ./test.sh
-rwxr-xr-x 1 fin fin 37 Sep 27 16:46 test.sh

$ cat ./test.sh
ps | grep "$$" | awk '{ print $4 }'

$ ./test.sh
bash

$ sudo ./test.sh
sh

最後の出力も、root が を使用していることを示しているbashためであるはずですが、 について何か見落としている点はありますか?/etc/passwdbashsudo

答え1

これは、Linux では のように定義されている を使用_PATH_BSHELLします。これは、たとえばまたはで実行した場合と同じになります。execvp()/bin/sh/usr/include/paths.henvfind -exec

bashユーザーのログイン シェルは絶対に使用しないでください。上記で確認した事実は、bash(そのコマンド ラインを入力したシェルが) それを実行しようとし、そこENOEXECからエラー コードを取得すると、代わりに (互換モードexecveで) それ自体で解釈することにしたためです。sh

答え2

-sオプションを使用しないため、( Ubuntu 12.04 LTS で定義されている)sudoを使用して実行するように設定します。ソース コードを確認すると、次のようになります。_PATH_BSHELL/usr/include/paths.h$SHELLsudo

/* Stash user's shell for use with the -s flag; don't pass to plugin. */
    if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
    ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
    }

-sオプションを使用する場合は、の代わりにsudoを使用します:$SHELL_PATH_BSHELL

$ cat ./test.sh
ps | grep "$$" | awk '{ print $4 }'

$ ./test.sh
bash

$ sudo -s ./test.sh
bash

答え3

カーネルはバイナリ実行可能イメージのみを実行できます。では、スクリプトはどのように実行されるのでしょうか。結局のところ、my_script_without_shebangシェル プロンプトで入力してもエラーは発生しませんENOEXEC。スクリプトの実行はカーネルではなく、シェルによって行われます。シェルの実行コードは通常、次のようになります。

/* try to run the program */
execl(program, basename(program), (char *)0);

/* the exec failed -- maybe it is a shell script without shebang? */
if (errno == ENOEXEC)
    execl ("/bin/sh", "sh", "-c", program, (char *)0);

シェバンなしのダミー シェル スクリプトをトレースすることで、これを検証できます。

cat > /tmp/foo.sh <<EOF
echo
EOF

chmod u+x /tmp/foo.sh

strace /tmp/foo.sh 2>&1 | grep exec
execve("/tmp/foo.sh", ["/tmp/foo.sh"], [/* 28 vars */]) = -1 ENOEXEC (Exec format error)

その後、実行は Stephane が説明したとおりに進行します。つまり、デフォルトのシェルが使用されます (上記のコード スニペットではハードコードされています)。この素晴らしいUNIX FAQさらに詳しくお答えできます。

関連情報