bash script: resultados diferentes quando chamados com ou sem sudo

10

No Ubuntu 16.04.3, eu tenho um script muito simples:

test.sh

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

Quando eu chamo de usuário não-root me ou como root , ele funciona como esperado. Se eu usar sudo ./test.sh , ele reclama de um erro de sintaxe:

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

O que poderia estar causando isso? Como posso corrigi-lo para que me possa usar esse script normalmente e com sudo ?

    
por James Newton 25.11.2017 / 14:40

2 respostas

20

Todo script começa com um Shebang , sem ele o shell inicia seu script não sabe qual interpretador deve executar seu script 1 e pode - como no caso de sudo ./script.sh here - executá-lo com sh , que no Ubuntu 16.04 está vinculado a dash . A expressão condicional [[ é uma bash comando composto , portanto, dash não sabe como lidar com isso e exibe o erro encontrado.

A solução aqui é adicionar

#!/bin/bash

como a primeira linha do seu script. Você pode obter o mesmo resultado quando você o chamar explicitamente com sudo bash ./script.sh , mas um shebang é o caminho a percorrer.
Para verificar qual shell executa seu script, adicione echo $0 a ele. Isso não é o mesmo que echo $SHELL , citando wiki.archlinux.org :

% bl0ck_qu0te%

1: Quando você iniciou ./test.sh com bash , apenas assumiu bash , o mesmo vale para o subgrupo sudo su .

    
por dessert 25.11.2017 / 14:50
5

Como @dessert explicou , o problema aqui é que o seu script não tem um linha shebang . Sem um shebang, sudo será o padrão para tentar executar o arquivo usando /bin/sh . Não consegui encontrá-lo documentado em nenhum lugar, mas confirmei verificando o código-fonte sudo , no qual encontrei o seguinte no arquivo pathnames.h :

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

Isso significa "definir se a variável _PATH_BSHELL não estiver definida, defina como /bin/sh ". Em seguida, no script configure incluído no tarball de origem, temos:

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

Esse loop procurará /bin/bash , /usr/bin/sh , /sbin/sh , /usr/sbin/sh ou /bin/ksh e, em seguida, definirá a _PATH_BSHELL para o que tiver sido encontrado primeiro . Como /bin/sh foi o primeiro da lista e existe, _PATH_BSHELL está definido como /bin/sh . O resultado de tudo isso é que o shell padrão de sudo , a menos que seja definido de outra forma, é /bin/sh .

Portanto, sudo será o padrão para executar as coisas usando /bin/sh e, no Ubuntu, é um link simbólico para dash , um shell compatível com POSIX mínimo:

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

A construção [[ é um recurso bash, não é definida pelo padrão POSIX e não é entendida por dash :

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

Em detalhes, nas três invocações que você tentou:

  1. ./test.sh

    Sem sudo ; na ausência de uma linha shebang, seu shell tentará executar o arquivo em si. Como você está executando bash , isso irá executar bash ./test.sh e funcionar.

  2. sudo su seguido por ./test.sh .

    Aqui, você está iniciando um novo shell para o usuário root . Este será qualquer shell definido na variável de ambiente $SHELL para esse usuário e, no Ubuntu, o shell padrão do root é bash :

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

    Aqui, você está permitindo que sudo execute o comando diretamente. Como o shell padrão é /bin/sh , conforme explicado acima, isso faz com que ele execute o script com /bin/sh , que é dash e falha, pois dash não entende [[ .

Nota : os detalhes de como sudo define o shell padrão parecem ser um pouco mais complexos. Tentei alterar os arquivos mencionados na minha resposta para apontar para /bin/bash , mas sudo ainda estava sendo padronizado para /bin/sh . Portanto, deve haver alguns outros locais no código-fonte em que o shell padrão está definido. No entanto, o ponto principal (que sudo padroniza para sh ) ainda permanece.

    
por terdon 25.11.2017 / 18:07