script bash: resultados diferentes cuando se llama con o sin sudo

10

En Ubuntu 16.04.3, tengo un script bash muy simple:

test.sh

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

Cuando lo llamo como usuario no root meo como root, funciona como se esperaba. Si lo uso sudo ./test.sh, se queja de un error de sintaxis:

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

¿Qué podría estar causando esto? ¿Cómo puedo solucionarlo para que mepueda usar este script tanto normalmente como con sudo?

James Newton
fuente
3
Consejo profesional: no tiene sentido corrersudo su . Solo corre sudo -io en su sudo -slugar.
terdon
@terdon sudo -icambia la ubicación a /root. sudo suo sudo -sno cambie la ubicación del directorio.
James Newton el
Sí, lea la pregunta a la que me relacioné antes para saber por qué Y lo siento, he editado mi comentario anterior, me había olvidado de mencionar -s.
terdon

Respuestas:

20

Cada script comienza con un Shebang , sin él, el shell que inicia su script no sabe qué intérprete debe ejecutar su script 1 y podría, como en el caso de sudo ./script.shaquí, ejecutarlo con sh, que en Ubuntu 16.04 está vinculado dash. La expresión condicional [[ es un bashcomando compuesto , por lo dashque no sabe cómo manejarlo y arroja el error que encontró.

La solución aquí es agregar

#!/bin/bash

como la primera línea de tu guión. Puede obtener el mismo resultado cuando lo llama explícitamente con sudo bash ./script.sh, pero un shebang es el camino a seguir.
Para verificar qué shell ejecuta su script, agréguelo echo $0. Eso no es lo mismo queecho $SHELL , citando wiki.archlinux.org :

SHELL contiene la ruta al shell preferido del usuario. Tenga en cuenta que este no es necesariamente el shell que se está ejecutando actualmente, aunque Bash establece esta variable en el inicio.

1: Como comenzaste ./test.shcon bashesto, simplemente asumiste bash, lo mismo ocurre con la sudo susubshell.

postre
fuente
1
También tenga en cuenta que bash ejecuta un script sin shebang usando bash, no /bin/sh.
muru
@dessert Eso lo arregla. ¡Gracias! ¿Cómo puedo verificar desde un script qué shell lo está ejecutando? ( echo $0Me da el nombre de la secuencia de comandos: ./test.sh)
James Newton
@JamesNewton no es una forma portátil, AFAIK, pero puedes verificar a qué /proc/$$/exepuntos. También puede probar varias variables como $BASH_VERSION, $ZSH_VERSIONetc. (pero el guión no establece ninguna de esas variables)
muru
5

Como explicó @dessert , el problema aquí es que su script no tiene una línea shebang . Sin un shebang, por sudodefecto intentará ejecutar el archivo usando /bin/sh. No pude encontrarlo documentado en ninguna parte, pero lo confirme comprobando el sudocódigo fuente donde encontré lo siguiente en el archivo pathnames.h:

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

Esto significa "establecer si la variable _PATH_BSHELLno está definida, establecerla en /bin/sh". Luego, en el configurescript incluido en el tarball fuente, tenemos:

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 bucle buscará /bin/bash, /usr/bin/sh, /sbin/sh, /usr/sbin/sho /bin/kshy selecciona el _PATH_BSHELLque lo que se encontró por primera vez . Dado que /bin/shfue el primero en la lista y existe, _PATH_BSHELLse establece en /bin/sh. El resultado de todo esto es que el shell predeterminado de a sudomenos que se defina lo contrario es /bin/sh.

Por lo tanto, sudose ejecutará de manera predeterminada las cosas usando /bin/shy, en Ubuntu, que es un enlace simbólico a dashun shell compatible con POSIX mínimo:

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

La [[construcción es una característica bash, no está definida por el estándar POSIX y no se entiende por dash:

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

En detalle, en las tres invocaciones que probaste:

  1. ./test.sh

    No sudo; en ausencia de una línea shebang, su shell intentará ejecutar el archivo en sí. Como está ejecutando bash, esto funcionará bash ./test.shy funcionará de manera efectiva .

  2. sudo suseguido de ./test.sh.

    Aquí, está comenzando un nuevo shell para el usuario root. Esto será cualquier shell definido en la $SHELLvariable de entorno para ese usuario y, en Ubuntu, el shell predeterminado de root es bash:

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

    Aquí, está permitiendo sudoejecutar el comando directamente. Dado que su shell predeterminado es /bin/shcomo se explicó anteriormente, esto hace que ejecute el script con /bin/sh, que es dashy falla, ya dashque no comprende [[.


Nota : los detalles de cómo sudoestablece el shell predeterminado parecen ser un poco más complejos. Traté de cambiar los archivos mencionados en mi respuesta para señalar /bin/bashpero sudotodavía estaba predeterminado /bin/sh. Por lo tanto, debe haber otros lugares en el código fuente donde se define el shell predeterminado. Sin embargo, el punto principal (que por sudodefecto es sh) sigue en pie.

terdon
fuente
No pude encontrarlo documentado en ninguna parte , yo tampoco, solo asumí que usaría /bin/shel mensaje de error, ¿qué otra cosa podría ser? La pregunta se responde maravillosamente en Qué shell usa sudo · SO , vea también la man sudosección EJECUCIÓN DE MANDOS . Resulta sudoque no usa un caparazón intermedio .
postre
1
@dessert yes, usa su propia implementación de la execvellamada al sistema que está predeterminada sh. Y no, los shells intermedios son irrelevantes, no se trata del shell que ejecuta el comando sino del intérprete de shell utilizado para leer el script de shell dado. Entonces, no, no inicia un shell intermedio, pero aún necesita un intérprete de shell para los scripts de shell.
terdon