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?


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 виконує скрипт без shebang, використовуючи bash, ні /bin/sh.
муру

@dessert Це виправляє. Дякую! Як я можу перевірити в скрипті, яка оболонка запущена? ( echo $0Дає мені назву сценарію: ./test.sh)
Джеймс Ньютон

@JamesNewton немає портативного способу, AFAIK, але ви можете перевірити, на що /proc/$$/exeвказує. Також ви можете протестувати різні змінні, наприклад $BASH_VERSION, $ZSH_VERSIONetc. (але тире не встановлює жодної такої змінної)
muru

5

Як пояснив @dessert , проблема тут полягає в тому, що у вашого сценарію немає рядка shebang . Без шебангу, 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) все ще залишається.


Я не міг знайти його документально нікуди - ні я, ні просто я припустив, що він буде використаний /bin/shіз повідомлення про помилку - що ще це могло бути? На це питання чудово відповідають відповіді: Яку оболонку використовує судо · ТАК , див. Також man sudoрозділ КОМАНДА ВИКОНАННЯ . Виявляється, sudoне використовується проміжна оболонка !
десерт

1
@dessert так, він використовує власну реалізацію execveсистемного виклику, для якого за замовчуванням sh. І ні, проміжні оболонки не мають значення. Це не про оболонку, яка виконує команду, а про інтерпретатор оболонки, який використовується для читання заданого сценарію оболонки. Отже, ні, він не запускає проміжну оболонку, але все ще потрібен інтерпретатор оболонок для скриптів оболонок.
тердон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.