скрипт bash: разные результаты при вызове с или без sudo

10

В Ubuntu 16.04.3 у меня есть очень простой скрипт bash:

test.sh

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

Когда я называю это как пользователь без meполномочий root или как 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 sudo -iменяет местоположение на /root. sudo suили sudo -sне меняйте местоположение каталога.
Джеймс Ньютон
Да, прочитайте вопрос, на который я ссылался ранее, почему И извините, я отредактировал свой предыдущий комментарий, я забыл упомянуть -s.
тердон

Ответы:

20

Каждый сценарий начинается с Shebang , без него оболочка, запускающая ваш сценарий, не знает, какой интерпретатор должен запустить ваш сценарий 1, и может - как в случае sudo ./script.shздесь - запустить его, с shкоторым в Ubuntu 16.04 связано dash. Условное выражение [[ является bashсоставной командой , поэтому dashне знает , как справиться с этим и бросает ошибку вы столкнулись.

Решение здесь состоит в том, чтобы добавить

#!/bin/bash

в первой строке вашего скрипта. Вы можете получить тот же результат, когда вы явно его называете sudo bash ./script.sh, но шебанг - это путь.
Чтобы проверить, какая оболочка запускает ваш скрипт, добавьте echo $0в него. Это не то же самое echo $SHELL , что со ссылкой на wiki.archlinux.org :

SHELL содержит путь к предпочтительной оболочке пользователя. Обратите внимание, что это не обязательно оболочка, которая в данный момент работает, хотя Bash устанавливает эту переменную при запуске.

1: Как вы начали ./test.shс bashэто только предположил , что bash, то же самое относится и к sudo suсубоболочке.

Десерт
источник
1
Также обратите внимание, что bash запускает скрипт без Шебанга, используя bash, а не /bin/sh.
Муру
@dessert Это исправляет. Спасибо! Как я могу проверить из скрипта, какая оболочка его запускает? ( echo $0Дает мне название сценария: ./test.sh)
Джеймс Ньютон
@JamesNewton нет портативного способа, AFAIK, но вы можете проверить, на что /proc/$$/exeуказывает. Также вы можете тестировать различные переменные, такие как $BASH_VERSION, $ZSH_VERSIONи т. Д. (Но dash не устанавливает такую ​​переменную)
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скрипте, включенном в исходный архив, мы имеем:

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 и не понимают dash:

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

Подробно, в трех вызовах, которые вы пробовали:

  1. ./test.sh

    Нет sudo; в отсутствие строки shebang ваша оболочка попытается выполнить сам файл. Поскольку вы работаете bash, это будет эффективно работать bash ./test.shи работать.

  2. sudo suс последующим ./test.sh.

    Здесь вы запускаете новую оболочку для пользователя root. Это будет оболочка, определенная в $SHELLпеременной окружения для этого пользователя, а в Ubuntu оболочкой root по умолчанию является 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) все еще остается в силе.

terdon
источник
Я нигде не мог найти документально подтвержденное - я тоже, я просто предполагал, что он будет использовать /bin/shиз сообщения об ошибке - что еще это может быть? Ответ на этот вопрос прекрасно дан в разделе « Какую оболочку использует sudo · SO» , см. Также man sudoраздел «ИСПОЛНЕНИЕ КОМАНД» . Оказывается, sudoне использует промежуточную оболочку !
десерт
1
@dessert yes, он использует собственную реализацию execveсистемного вызова, по умолчанию sh. И нет, промежуточные оболочки не имеют значения, речь идет не об оболочке, которая выполняет команду, а о интерпретаторе оболочки, используемом для чтения данного сценария оболочки. Так что нет, он не запускает промежуточную оболочку, но ему все еще нужен интерпретатор оболочки для сценариев оболочки.
тердон