bashスクリプト:sudoの有無にかかわらず呼び出されたときの結果が異なる


10

Ubuntu 16.04.3では、非常に単純なbashスクリプトがあります。

test.sh

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

非rootユーザーmeまたはとして呼び出すとroot、期待どおりに動作します。私が使用するsudo ./test.shと、構文エラーについて文句を言います:

$ ./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

何が原因でしょうか?どのように修正すれば、meこのスクリプトを通常と一緒に使用できますsudoか?


3
プロのヒント:実行しても意味がありませんsudo su。ただ実行するsudo -iか、sudo -s代わりに。
terdon 2017年

@terdon sudo -iは場所をに変更します/rootsudo suまたはsudo -s、ディレクトリの場所を変更しないでください。
ジェームズニュートン

はい、その理由について前にリンクした質問を読みます。申し訳ありませんが、以前のコメントを編集しました-s。言及するのを忘れていました。
terdon 2017年

回答:


20

すべてのスクリプトは、で始まるシェバングそれなしで、あなたのスクリプトを実行すべきインタプリタ知らないスクリプトを起動するシェル1の場合のように-と威力をsudo ./script.shここに-と、それを実行しsh、Ubuntuの16.04でにリンクされていますdash条件式は、 [[あるbash化合物のコマンドので、dashそれを処理する方法を知らないと、あなたが遭遇したエラーをスローします。

ここでの解決策は追加することです

#!/bin/bash

スクリプトの最初の行として。で明示的に呼び出すと同じ結果が得られる場合がありますsudo bash ./script.shが、シバンはその方法です。
スクリプトを実行するシェルを確認するにはecho $0、それに追加します。wiki.archlinux.orgを引用し echo $SHELL、それはと同じではありません

SHELLには、ユーザーの優先シェルへのパスが含まれています。Bashは起動時にこの変数を設定しますが、これは必ずしも現在実行中のシェルではないことに注意してください。

1:想定./test.shbashたところから始めたようbashに、sudo suサブシェルも同様です。


1
また、bashは、bashを使用せず、bashを使用せずにスクリプトを実行することに注意してください/bin/sh
ムル

@dessert修正します。ありがとう!スクリプト内からどのシェルがそれを実行しているかを確認するにどうすればよいですか?(echo $0スクリプトの名前を教えてください:./test.sh
James Newton

@JamesNewtonポータブルな方法はありません/proc/$$/exe。また、次のような様々な変数をテストすることができ$BASH_VERSION$ZSH_VERSIONなどを、(ただし、ダッシュは、そのような変数を設定しない)
muru

5

以下のよう@dessertは説明し、ここでの問題は、あなたのスクリプトが持っていないということですシェバング行を。シバンがない場合、sudoデフォルトではを使用してファイルを実行しようとします/bin/sh。どこにも文書化されていませんでしたsudoが、次のファイルのソースコードを確認して確認しましたpathnames.h

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

これは、「変数_PATH_BSHELLが定義されていない場合は設定し、それを設定する/bin/sh」ことを意味します。次に、configureソースtarballに含まれているスクリプトに次のように記述します。

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

このループは探します/bin/bash/usr/bin/sh/sbin/sh/usr/sbin/shまたは/bin/kshその後、設定_PATH_BSHELL最初に発見された方。以来/bin/sh、リスト内の最初だったし、それが存在する、_PATH_BSHELLに設定されています/bin/sh。このすべての結果として、sudo特に定義されていない限り、デフォルトのシェルはになります/bin/sh

したがって、sudoデフォルト/bin/shでは、Ubuntuではdash最小のPOSIX準拠シェルを使用して実行します。

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

[[構築物は、それはPOSIX標準で定義されていないとが理解されていない、bashの機能ですdash

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

詳細には、次の3つの呼び出しで試しました。

  1. ./test.sh

    いいえsudo; シバン行がない場合、シェルはファイル自体を実行しようとします。を実行しているのでbash、これは効果的に実行されbash ./test.sh、機能します。

  2. sudo suが続き./test.shます。

    ここでは、ユーザーの新しいシェルを開始していますroot。これは$SHELL、そのユーザーの環境変数で定義されているシェルです。Ubuntuでは、ルートのデフォルトシェルはbash次のとおりです。

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

    ここではsudo、コマンドを直接実行させています。デフォルトのシェルがされているので/bin/sh、上記のように説明し、これはそれがでスクリプトを実行する原因/bin/shである、dashとするので、それは失敗しdash理解していません[[


sudoデフォルトのシェルを設定する方法の詳細は、もう少し複雑に見えます。私の回答で言及されているファイルをポイントするように変更しようとしまし/bin/bashsudoが、それでもデフォルトになってい/bin/shます。そのため、デフォルトのシェルが定義されているソースコード内の他の場所が必要です。それにもかかわらず、要点(sudoデフォルトはsh)はまだ残っています。


私はどこにも文書化されているのを見つけることができませんでした–私もどちらも、/bin/shエラーメッセージからそれを使用すると思いました–他に何が可能でしょうか?質問は、sudoが使用するシェルは何ですか?SOで美しく答えられます。「man sudoコマンドの実行」のセクションも参照してください。sudo中間シェルを使用しないことがわかりました!
デザート

1
@dessertはい、execveデフォルトでに設定されているシステムコールの独自の実装を使用しshます。そして、いいえ、中間シェルは無関係です。これは、コマンドを実行するシェルの問題ではなく、指定されたシェルスクリプトの読み取りに使用されるシェルインタープリターの問題です。したがって、中間シェルは起動しませんが、シェルスクリプト用のシェルインタープリターが必要です。
terdon 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.