bash script: разные результаты при вызове с или без 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 ?

    
задан James Newton 25.11.2017 в 13:40
источник

2 ответа

20

Каждый скрипт начинается с 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

SHELL contains the path to the user's preferred shell. Note that this is not necessarily the shell that is currently running, although Bash sets this variable on startup.

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

    
ответ дан dessert 25.11.2017 в 13:50
источник
5

Как @dessert объяснил , проблема здесь в том, что ваш скрипт не имеет строка shebang . Без shebang 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

Конструкция [[ является функцией bash, она не определяется стандартом 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 25.11.2017 в 17:07