#! / bin / sh vs #! / bin / bash para máxima portabilidad

cherouvim 07/30/2017. 3 answers, 2.886 views
linux bash shell sh

Normalmente trabajo con Ubuntu LTS servidores que de lo que entiendo symlink /bin/sh a /bin/dash . Muchas otras distribuciones aunque symlink /bin/sh a /bin/bash .

A partir de eso, entiendo que si una secuencia de comandos utiliza #!/bin/sh en la parte superior puede no ejecutarse de la misma manera en todos los servidores?

¿Existe una práctica sugerida en qué shell utilizar para los scripts cuando uno quiere la máxima portabilidad de los scripts entre servidores?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Hay ligeras diferencias entre las diferentes conchas. Si la portabilidad es la más importante para usted, utilice #!/bin/sh y no use nada más que lo que el shell original proporcionó.

3 Answers


Gordon Davisson 08/01/2017.

Hay aproximadamente cuatro niveles de portabilidad para los scripts de shell (en lo que respecta a la línea shebang):

  1. La mayoría de portátiles: use un #!/bin/sh shebang y utilice only la sintaxis básica del shell especificada en el estándar POSIX . Esto debería funcionar en prácticamente cualquier sistema POSIX / unix / linux. (Bueno, excepto Solaris 10 y versiones anteriores que tenían el shell Bourne legado real, anterior a POSIX así que no compatible, como /bin/sh .)

  2. Segunda más portátil: use una línea #!/bin/bash (o #!/usr/bin/env bash ) basándose en las características de bash v3. Esto funcionará en cualquier sistema que tenga bash (en la ubicación esperada).

  3. Tercero más portátil: use una línea shebang #!/bin/bash (o #!/usr/bin/env bash ) y utilice las funciones de bash v4. Esto va a fallar en cualquier sistema que tiene bash v3 (por ejemplo, macOS, que tiene que utilizarlo para las razones de licencia).

  4. Menos portátil: use un #!/bin/sh shebang y utilice las extensiones de bash a la sintaxis de la shell POSIX. Esto fallará en cualquier sistema que tenga algo más que bash para / bin / sh (como las recientes versiones de Ubuntu). Nunca lo hagas; no es sólo un problema de compatibilidad, es simplemente erróneo. Desafortunadamente, es un error que mucha gente comete.

Mi recomendación: utilice el más conservador de los tres primeros que suministra todas las características de shell que necesita para el script. Para la portabilidad máxima, utilice la opción # 1, pero en mi experiencia algunas características de bash (como arrays) son bastante provechosas que iré con # 2.

Lo peor que puedes hacer es # 4, usando el shebang equivocado. Si no estás seguro de cuáles son las características básicas de POSIX y cuáles son las extensiones de bash, póngase con una bash shebang (es decir, la opción # 2) o pruebe la secuencia de comandos a fondo con una shell muy básica (como el guión en los servidores Ubuntu LTS). El wiki de Ubuntu tiene una buena lista de bashisms a tener en cuenta .

Hay una muy buena información sobre la historia y las diferencias entre shells en la pregunta de Unix y Linux "¿Qué significa ser compatible con sh?" y la pregunta Stackoverflow "Diferencia entre sh y bash" .

Además, tenga en cuenta que el shell no es lo único que difiere entre los diferentes sistemas; si estás acostumbrado a linux, estás acostumbrado a los comandos GNU, que tienen muchas extensiones no estándar que no puedes encontrar en otros sistemas unix (por ejemplo, bsd, macOS). Desafortunadamente, no hay una regla simple aquí, sólo tienes que saber el rango de variación de los comandos que estás usando.

Uno de los comandos más desagradables en términos de portabilidad es uno de los más básicos: echo . Cada vez que lo use con cualquier opción (por ejemplo, echo -n o echo -e ), o con cualquier escape (barras invertidas) en la cadena para imprimir, las diferentes versiones harán cosas diferentes. Cada vez que quiera imprimir una cadena sin un salto de línea después de ella, o con escapes en la cadena, use printf lugar (y aprender cómo funciona - es más complicado que el echo ). El comando ps también es un desastre .

Otra cosa general a la hora de mirar es la sintaxis reciente / GNUish de las extensiones a la opción de comando: el formato de comando antiguo (estándar) es que el comando es seguido de opciones (con un solo guión y cada opción es una sola letra), seguido de argumentos de comando. Las variantes recientes (ya menudo no portátiles) incluyen opciones largas (usualmente introducidas con -- ), permitiendo que las opciones vienen después de argumentos, y usando -- para separar opciones de argumentos.

5 comments
12 pabouk 07/30/2017
La cuarta opción es simplemente una idea equivocada. Por favor no lo use.
3 Gordon Davisson 07/30/2017
@pabouk Estoy totalmente de acuerdo, por lo que editado mi respuesta para hacer esto más claro.
1 jlliagre 07/30/2017
Su primera declaración es un poco engañosa. El estándar POSIX no especifica nada sobre el shebang fuera de decirlo, lo que lleva a un comportamiento no especificado. Por otra parte, POSIX no especifica dónde se debe ubicar el shell posix, solo su nombre ( sh ), por lo que /bin/sh no se garantiza que sea el camino correcto. El más portátil es entonces no especificar ningún shebang en absoluto, o adaptar el shebang al OS usado.
2 Michael Kjörling 07/30/2017
Me caí en el # 4 con un guión de la mía muy recientemente, y simplemente no podía entender por qué no funcionaba; después de todo, los mismos comandos funcionaban sólidamente, e hicieron exactamente lo que quería que hicieran, cuando los probé directamente en el shell. Tan pronto como cambié #!/bin/sh a #!/bin/bash sin embargo, el script funcionó perfectamente. (Para mi defensa, ese guión había evolucionado en el tiempo de uno que realmente sólo necesitaba sh-ismos, a uno que confiaba en un comportamiento de tipo bash).
2 jlliagre 07/30/2017
@ MichaelKjörling El (verdadero) shell Bourne casi nunca está incluido con distribuciones de Linux y no es compatible con POSIX de todos modos. El shell estándar POSIX se creó a partir del comportamiento ksh , no de Bourne. Lo que la mayoría de las distribuciones de Linux después de la FHS hacen es tener /bin/sh siendo un enlace simbólico al shell que seleccionan para proporcionar compatibilidad POSIX, normalmente bash o dash .

Kaz 07/31/2017.

En el script ./configure que prepara el lenguaje TXR para la construcción, escribí el siguiente prólogo para una mejor portabilidad. La secuencia de comandos se arrancará incluso si #!/bin/sh es un antiguo shell Bourne que no cumple con POSIX. (Construyo cada versión en una máquina virtual de Solaris 10).

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell 

La idea aquí es encontrar un shell mejor que el que estamos ejecutando y volver a ejecutar el script usando ese shell. Se txr_shell la variable de entorno txr_shell , de modo que el script re-ejecutado sabe que es la instancia recursiva re-ejecutada.

(En mi script, la variable txr_shell también se utiliza posteriormente, para dos propósitos: en primer lugar, se imprime como parte de un mensaje informativo en la salida del script.En segundo lugar, se instala como la variable SHELL en el Makefile , por lo que make utilizará esta shell también para ejecutar recetas.)

En un sistema donde /bin/sh es guión, puede ver que la lógica anterior encontrará /bin/bash y volver a ejecutar el script con eso.

En un cuadro de Solaris 10, el /usr/xpg4/bin/sh se iniciará si no se encuentra Bash.

El prólogo está escrito en un dialecto conservador de la cáscara, usando la test para las test de la existencia del archivo, y el truco de ${@+"$@"} para los argumentos de extensión que abastecen a algunas cáscaras viejas quebradas (que sería simplemente "$@" si fuéramos en un shell que cumple con POSIX).

2 comments
Charles Duffy 07/31/2017
Uno no necesitaría la x hackery si se usaban citas apropiadas, ya que las situaciones que obligaban a ese idioma a rodear ahora las llamadas de prueba obsoletas con -a o -o combinando múltiples pruebas.
Kaz 07/31/2017
@CharlesDuffy De hecho; la test x$whatever que estoy perpetrando allí parece una cebolla en el barniz. Si no podemos confiar en la vieja concha rota para hacer citas, entonces el intento final de ${@+"$@"} es inútil.

zwol 07/31/2017.

Todas las variaciones del lenguaje de shell Bourne son objetivamente terribles en comparación con los lenguajes de scripting modernos como Perl, Python, Ruby, node.js e incluso (posiblemente) Tcl. Si tiene que hacer algo incluso un poco complicado, será más feliz en el largo plazo si utiliza uno de los anteriores en lugar de un script de shell.

La única ventaja que el lenguaje shell aún tiene sobre los nuevos idiomas es que something que se llama /bin/sh está garantizado para existir en cualquier cosa que pretenda ser Unix. Sin embargo, ese algo no puede incluso ser compatible con POSIX; muchos de los Unixes propietarios heredados congelaron el lenguaje implementado por /bin/sh y las utilidades en el path por defecto prior de los cambios demandados por Unix95 (sí, Unix95, hace veinte años y contando). Puede haber un conjunto de Unix95, o incluso POSIX.1-2001 si tienes suerte, las herramientas en un directorio not en el /usr/xpg4/bin por defecto (por ejemplo, /usr/xpg4/bin ), pero no se garantiza su existencia.

Sin embargo, los fundamentos de Perl tienen more likely de estar presentes en una instalación de Unix seleccionada arbitrariamente que Bash. (Por "los conceptos básicos de Perl" me refiero a /usr/bin/perl existe y es some , posiblemente bastante antigua, la versión de Perl 5, y si tienes suerte el conjunto de módulos que se envían con esa versión del intérprete también son disponible.)

Por lo tanto:

Si está escribiendo algo que tiene que trabajar en everywhere que pretenda ser Unix (como un script "configure"), necesita usar #! /bin/sh #! /bin/sh , y usted no necesita usar extensiones cualesquiera. Hoy en día escribiría shell POSIX.1-2001-compliant en esta circunstancia, pero estaría preparado para reparar POSIXisms si alguien pidió ayuda para el hierro oxidado.

Pero si usted not está escribiendo algo que tiene que trabajar en todas partes, entonces en el momento en que se sienta tentado a usar cualquier Bashism en absoluto, debe detener y reescribir la cosa entera en un mejor lenguaje de secuencias de comandos en su lugar. Tu futuro te lo agradecerá.

(Por lo tanto, ¿cuándo is apropiado utilizar las extensiones de Bash? En primer orden: nunca En segundo orden: sólo para extender el entorno interactivo Bash, por ejemplo, para proporcionar tabulación inteligente y sugerencias de fantasía.

Related questions

Hot questions

Language

Popular Tags