script bash: risultati diversi se chiamato con o senza sudo


10

In Ubuntu 16.04.3, ho uno script bash molto semplice:

test.sh

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

Quando lo chiamo come utente non root meo come root, funziona come previsto. Se uso sudo ./test.sh, si lamenta di un errore di sintassi:

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

Che cosa potrebbe causare questo? Come posso ripararlo in modo che mepossa usare questo script sia normalmente che con sudo?


3
Suggerimento professionale: non ha senso correresudo su . Basta correre sudo -io sudo -sinvece.
terdon,

@terdon sudo -icambia la posizione in /root. sudo suo sudo -snon modificare la posizione della directory.
James Newton,

Sì, leggi la domanda che ho collegato prima per il perché. E scusa, ho modificato il mio commento precedente, avevo dimenticato di menzionare -s.
terdon,

Risposte:


20

Ogni script inizia con uno Shebang , senza di esso la shell che avvia il tuo script non sa con quale interprete dovrebbe eseguire lo script 1 e potrebbe - come nel caso di sudo ./script.shqui - eseguirlo sh, a cui in Ubuntu 16.04 è collegato dash. L' espressione condizionale [[ è un bashcomando composto , quindi dashnon sa come gestirlo e genera l'errore riscontrato.

La soluzione qui è aggiungere

#!/bin/bash

come prima riga della tua sceneggiatura. Puoi ottenere lo stesso risultato quando lo chiami esplicitamente con sudo bash ./script.sh, ma uno shebang è la strada da percorrere.
Per verificare quale shell esegue il tuo script, aggiungilo echo $0. Non è la stessa cosa echo $SHELL , citando wiki.archlinux.org :

SHELL contiene il percorso della shell preferita dell'utente. Nota che questa non è necessariamente la shell attualmente in esecuzione, anche se Bash imposta questa variabile all'avvio.

1: Come hai iniziato ./test.shcon bashesso appena assunto bash, lo stesso vale per la sudo susubshell.


1
Nota anche che bash esegue uno script senza shebang usando bash, no /bin/sh.
muru,

@dessert Questo lo risolve. Grazie! Come posso verificare all'interno di uno script quale shell lo sta eseguendo? ( echo $0Mi dà il nome dello script: ./test.sh)
James Newton

@JamesNewton nessun modo portatile, AFAIK, ma puoi controllare a cosa /proc/$$/exepunta. Inoltre è possibile testare diverse variabili come $BASH_VERSION, $ZSH_VERSIONecc (ma trattino non impostare tale variabile)
Muru

5

Come ha spiegato @dessert , il problema qui è che la tua sceneggiatura non ha una linea shebang . Senza shebang, sudoper impostazione predefinita tenterà di eseguire il file utilizzando /bin/sh. Non sono riuscito a trovarlo documentato da nessuna parte, ma ho confermato controllando il sudocodice sorgente in cui ho trovato quanto segue nel file pathnames.h:

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

Questo significa "imposta se la variabile _PATH_BSHELLnon è definita, impostala su /bin/sh". Quindi, nello configurescript incluso nel tarball sorgente, abbiamo:

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

Questo ciclo cercherà /bin/bash, /usr/bin/sh, /sbin/sh, /usr/sbin/sho /bin/kshe quindi imposta la _PATH_BSHELLa seconda di quale è stato trovato prima . Poiché è /bin/shstato il primo nell'elenco ed esiste, _PATH_BSHELLè impostato su /bin/sh. Il risultato di tutto ciò è che la shell predefinita è sudose non diversamente definito /bin/sh.

Quindi, sudoper impostazione predefinita , eseguirà le cose usando /bin/she, su Ubuntu, che è un collegamento simbolico a dashuna shell minima compatibile con POSIX:

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

Il [[costrutto è una funzione bash, non è definito dallo standard POSIX e non è compreso da dash:

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

Nel dettaglio, nelle tre invocazioni che hai provato:

  1. ./test.sh

    No sudo; in assenza di una riga shebang, la shell tenterà di eseguire il file stesso. Dal momento che stai funzionando bash, questo funzionerà bash ./test.she funzionerà efficacemente .

  2. sudo suseguito da ./test.sh.

    Qui, stai avviando una nuova shell per l'utente root. Questo sarà qualunque shell definita nella $SHELLvariabile d'ambiente per quell'utente e, su Ubuntu, la shell predefinita di root è bash:

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

    Qui, stai lasciando sudoeseguire direttamente il comando. Poiché la sua shell predefinita è /bin/shcome spiegato sopra, questo fa sì che esegua lo script /bin/sh, che è dashe non riesce poiché dashnon capisce [[.


Nota : i dettagli di come sudoimposta la shell predefinita sembrano essere un po 'più complessi. Ho provato a cambiare i file menzionati nella mia risposta per puntare a, /bin/bashma sudoera ancora predefinito /bin/sh. Quindi ci devono essere altri posti nel codice sorgente in cui è definita la shell predefinita. Tuttavia, il punto principale ( sudopredefinito sh) rimane valido.


Non riuscivo a trovarlo documentato da nessuna parte - nemmeno io, supponevo che avrebbe usato /bin/shdal messaggio di errore - cos'altro potrebbe essere? Alla domanda si risponde magnificamente in Che shell usa sudo · SO , vedere anche man sudo, sezione ESECUZIONE DEI COMANDI . Si scopre sudoche non usa una shell intermedia !
dessert

1
@dessert sì, utilizza la propria implementazione della execvechiamata di sistema predefinita sh. E no, le shell intermedie sono irrilevanti, non si tratta della shell che esegue il comando ma dell'interprete della shell usato per leggere lo script della shell fornito. Quindi no, non avvia una shell intermedia ma necessita comunque di un interprete di shell per gli script di shell.
terdon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.