script bash: resultados diferentes quando chamados com ou sem sudo


10

No Ubuntu 16.04.3, tenho um script bash muito simples:

test.sh

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

Quando eu o chamo como usuário não raiz meou como root, ele funciona conforme o 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 mepossa usar esse script normalmente e com sudo?


3
Dica profissional: não faz sentido corrersudo su . Basta executar sudo -iou em sudo -svez disso.
terdon

@terdon sudo -ialtera a localização para /root. sudo suou sudo -snão altere o local do diretório.
James Newton

Sim, leia a pergunta à qual vinculei anteriormente e por quê. E desculpe, eu editei meu comentário anterior, esqueci de mencionar -s.
terdon

Respostas:


20

Todo script começa com um Shebang , sem ele, o shell que inicia o script não sabe qual intérprete deve executar o seu script 1 e pode - como no caso sudo ./script.shaqui - executá-lo sh, ao qual o Ubuntu 16.04 está vinculado dash. A expressão condicional [[ é um bashcomando composto , portanto dash, não sabe como lidar com isso e lança o erro que você encontrou.

A solução aqui é adicionar

#!/bin/bash

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

SHELL contém o caminho para o shell preferido do usuário. Observe que este não é necessariamente o shell atualmente em execução, embora o Bash defina essa variável na inicialização.

1: Como você começou ./test.shcom bashele apenas assumiu bash, o mesmo vale para o sudo susubnível.


1
Observe também que o bash executa um script sem shebang usando o bash, não /bin/sh.
25717 muru

@dessert Isso corrige. Obrigado! Como posso verificar em um script qual shell está executando? ( echo $0Dá-me o nome do script: ./test.sh)
James Newton

@ JamesNewton de maneira portátil, AFAIK, mas você pode verificar para que serve /proc/$$/exe. Além disso, você pode testar várias variáveis como $BASH_VERSION, $ZSH_VERSION, etc. (mas traço não define qualquer variável)
Muru

5

Como o @dessert explicou , o problema aqui é que seu script não tem uma linha shebang . Sem um shebang, sudoo padrão é tentar executar o arquivo usando /bin/sh. Não consegui encontrá-lo documentado em nenhum lugar, mas confirmei verificando o sudocódigo-fonte onde 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_BSHELLnão estiver definida, defina-a como /bin/sh". Então, no configurescript 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

Este circuito irá procurar /bin/bash, /usr/bin/sh, /sbin/sh, /usr/sbin/shou /bin/kshe, em seguida, define o _PATH_BSHELLque o que foi encontrado pela primeira vez . Como /bin/shfoi o primeiro na lista e existe, _PATH_BSHELLestá definido como /bin/sh. O resultado de tudo isso é que o shell padrão, a sudomenos que seja definido de outra forma, é /bin/sh.

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

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

A [[construção é um recurso bash, não é definido pelo padrão POSIX e não é entendido 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

    Não sudo; na ausência de uma linha shebang, seu shell tentará executar o próprio arquivo. Como você está executando bash, isso funcionará bash ./test.she funcionará efetivamente .

  2. sudo suseguido por ./test.sh.

    Aqui, você está iniciando um novo shell para o usuário root. Este será o shell definido na $SHELLvariável de ambiente 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á deixando sudoexecutar o comando diretamente. Como seu shell padrão é /bin/shcomo explicado acima, isso faz com que ele execute o script /bin/sh, o que é dashe falha desde que dashele não entende [[.


Nota : os detalhes de como sudodefine o shell padrão parecem um pouco mais complexos. Tentei alterar os arquivos mencionados na minha resposta para apontar para, /bin/bashmas sudoainda era o padrão /bin/sh. Portanto, deve haver outros lugares no código-fonte em que o shell padrão está definido. No entanto, o ponto principal (que o sudopadrão é sh) ainda permanece.


Não consegui encontrá-lo documentado em nenhum lugar - nem eu, apenas presumi que usaria a /bin/shpartir da mensagem de erro - o que mais poderia ser possível? A pergunta é respondida lindamente em Qual shell o sudo usa · SO , consulte também a man sudoseção COMMAND EXECUTION . Acontece sudoque não usa um shell intermediário !
Dessert

1
@dessert sim, ele usa sua própria implementação da execvechamada de sistema à qual o padrão é sh. E não, shells intermediários são irrelevantes, não se trata do shell que executa o comando, mas do interpretador de shell usado para ler o script de shell fornecido. Portanto, não, ele não inicia um shell intermediário, mas ainda precisa de um interpretador de shell para scripts de shell.
terdon
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.