web-dev-qa-db-ja.com

bashスクリプト:sudoを使用して、または使用せずに呼び出した場合の異なる結果

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の両方でこのスクリプトを使用できるように修正するにはどうすればよいですか?

10
James Newton

すべてのスクリプトは Shebang で始まります。これがないと、スクリプトを開始するシェルは、どのインタープリターがスクリプトを実行すべきかを認識しません。1 そして、おそらくSudo ./script.shの場合のように、shで実行します。これはUbuntu 16.04ではdashにリンクされています。 条件式[[bash複合コマンド であるため、dashはその処理方法を認識せず、エラーをスローしますあなたが遭遇しました。

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

#!/bin/bash

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

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

1:./test.shbashで開始したとき、それはbashを想定しただ​​けで、Sudo suサブシェルについても同じことが言えます。

20
dessert

@ dessertの説明 のように、ここでの問題は、スクリプトに Shebang行 がないことです。 Shebangがない場合、Sudoはデフォルトで/bin/shを使用してファイルを実行しようとします。どこにも文書化されていませんでしたが、Sudoソースコードを確認することで確認しました。ソースコードでは、ファイルpathnames.hで次を見つけました。

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

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

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

[[コンストラクトはbash機能であり、POSIX標準では定義されておらず、dashで認識されません。

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

詳細には、あなたが試した3つの呼び出しで:

  1. ./test.sh

    いいえSudo; Shebang行がない場合、シェルはファイル自体を実行しようとします。 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/bashを指すように変更しようとしましたが、Sudoは依然として/bin/shにデフォルト設定されていました。そのため、ソースコードには、デフォルトのシェルが定義されている他の場所が必要です。それにもかかわらず、主要な点(Sudoがデフォルトでshに設定されていること)は依然として有効です。

5
terdon