skrypt bash: różne wyniki przy wywołaniu z lub bez sudo

10

W Ubuntu 16.04.3 mam bardzo prosty skrypt bash:

test.sh

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

Kiedy nazywam to jako użytkownik inny niż root melub jako root, działa zgodnie z oczekiwaniami. Jeśli użyję sudo ./test.sh, narzeka na błąd składniowy:

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

Co może być tego przyczyną? Jak mogę to naprawić, aby memożna było używać tego skryptu zarówno normalnie, jak i za pomocą sudo?

James Newton
źródło
3
Wskazówka: nie ma sensu biegaćsudo su . Po prostu uruchom sudo -ilub sudo -szamiast tego.
terdon
@terdon sudo -izmienia lokalizację na /root. sudo sulub sudo -snie zmieniaj lokalizacji katalogu.
James Newton,
Tak, przeczytaj pytanie, z którym wcześniej się łączyłem, dlaczego. I przepraszam, zredagowałem mój poprzedni komentarz, zapomniałem wspomnieć -s.
terdon

Odpowiedzi:

20

Każdy skrypt zaczyna się od Shebang , bez niego powłoka uruchamiająca skrypt nie wie, który interpreter powinien uruchomić twój skrypt 1, i może - tak jak w tym przypadku sudo ./script.sh- uruchomić go, z shktórym w Ubuntu 16.04 jest powiązany dash. Wyrażenie warunkowe [[ jest bashkomenda związek , więc dashnie wiem, jak sobie z tym poradzić i rzuca napotkany błąd.

Rozwiązaniem jest dodanie

#!/bin/bash

jako pierwszy wiersz skryptu. Możesz uzyskać ten sam wynik, gdy nazywasz go jawnie sudo bash ./script.sh, ale szlaban jest dobrym rozwiązaniem.
Aby sprawdzić, która powłoka uruchamia skrypt, dodaj echo $0do niego. To nie to samo, co echo $SHELL cytowanie wiki.archlinux.org :

SHELL zawiera ścieżkę do preferowanej powłoki użytkownika. Zauważ, że niekoniecznie jest to aktualnie działająca powłoka, chociaż Bash ustawia tę zmienną podczas uruchamiania.

1: Jak rozpoczęła ./test.shsię bashpo prostu założyć bash, to samo tyczy się sudo supodpowłoce.

deser
źródło
1
Zauważ też, że bash uruchamia skrypt bez shebang, używając bash, a nie /bin/sh.
mur
@ deser To naprawia. Dzięki! Jak mogę sprawdzić w skrypcie, która powłoka go uruchamia? ( echo $0Daje mi nazwę skryptu: ./test.sh)
James Newton
@JamesNewton nie ma przenośnego sposobu, AFAIK, ale możesz sprawdzić, na co /proc/$$/exewskazuje. Ponadto można przetestować różne zmienne, jak $BASH_VERSION, $ZSH_VERSIONitp (ale kreska nie ustala żadnych takich zmienna)
Muru
5

Jak wyjaśnił @dessert , problem polega na tym, że twój skrypt nie ma linii shebang . Bez shebang, sudodomyślnie będzie próbował uruchomić plik przy użyciu /bin/sh. Nigdzie nie mogłem go udokumentować, ale potwierdziłem, sprawdzając sudokod źródłowy, w którym znalazłem następujące informacje w pliku pathnames.h:

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

Oznacza to „ustaw, jeśli zmienna _PATH_BSHELLnie jest zdefiniowana, ustaw ją na /bin/sh”. Następnie w configureskrypcie zawartym w źródłowym pliku archiwalnym mamy:

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

Pętla ta wygląda na /bin/bash, /usr/bin/sh, /sbin/sh, /usr/sbin/shi /bin/ksh, a następnie ustawia _PATH_BSHELLsię w zależności od tego stwierdzono pierwszy . Ponieważ /bin/shbył pierwszy na liście i istnieje, _PATH_BSHELLjest ustawiony na /bin/sh. Wynikiem tego wszystkiego jest to, że domyślną powłoką jest sudochyba, że ​​zdefiniowano inaczej /bin/sh.

Tak więc sudodomyślnie uruchomi się za pomocą, /bin/sha na Ubuntu, który jest dowiązaniem symbolicznym do dashminimalnej powłoki zgodnej z POSIX:

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

[[Konstrukt funkcja atakujących, nie jest zdefiniowane przez standard POSIX i nie są rozumiane przez dash:

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

Szczegółowo w trzech wywołaniach, które próbowałeś:

  1. ./test.sh

    Nie sudo; w przypadku braku linii shebang, twoja powłoka spróbuje wykonać sam plik. Ponieważ działasz bash, będzie to działać skutecznie bash ./test.shi działać.

  2. sudo sua następnie ./test.sh.

    Tutaj zaczynasz nową powłokę dla użytkownika root. Będzie to dowolna powłoka zdefiniowana w $SHELLzmiennej środowiskowej dla tego użytkownika, a na Ubuntu domyślną powłoką roota jest bash:

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

    Tutaj pozwalasz sudona bezpośrednie wykonanie polecenia. Ponieważ jego domyślna powłoka jest taka, /bin/shjak wyjaśniono powyżej, powoduje to uruchomienie skryptu /bin/sh, co jest dashi kończy się niepowodzeniem, ponieważ dashnie rozumie [[.


Uwaga : szczegóły dotyczące sudoustawień domyślnej powłoki wydają się nieco bardziej złożone. Próbowałem zmienić pliki wymienione w mojej odpowiedzi, aby wskazywały, /bin/bashale sudonadal domyślnie /bin/sh. Dlatego w kodzie źródłowym muszą znajdować się inne miejsca, w których zdefiniowana jest domyślna powłoka. Niemniej jednak główny punkt (który sudodomyślnie jest sh) nadal obowiązuje.

terdon
źródło
Nigdzie nie znalazłem tego udokumentowanego - ja też, po prostu założyłem, że skorzysta /bin/shz komunikatu o błędzie - co jeszcze może być? Odpowiedź na to pytanie jest piękna w tym, z czego korzysta sudo · SO , patrz także man sudosekcja WYKONYWANIE POLECEŃ . Okazuje się, sudoże nie używa pośredniej powłoki !
deser
1
@dessert tak, używa własnej implementacji execvewywołania systemowego, która domyślnie jest ustawiona na sh. I nie, powłoki pośrednie nie mają znaczenia, nie chodzi o powłokę uruchamiającą polecenie, ale o interpreter powłoki używany do odczytu podanego skryptu powłoki. Więc nie, nie uruchamia powłoki pośredniej, ale nadal potrzebuje interpretera powłoki dla skryptów powłoki.
terdon