¿Cuándo debo usar #! / Bin / bash y cuándo #! / Bin / sh?

104

¿Cuándo es #!/bin/bashmás apropiado que #!/bin/shen un script de shell?

Hendré
fuente
55
Cuando utiliza bashfunciones y sintaxis en lugar de shfunciones y sintaxis.
Mokubai
3
Si ayuda a alguien, he notado que vimresaltará bash-ismos si su script tiene el #!/bin/shshebang. Solo lo cambio a bashsi las cosas se ponen lo suficientemente peludas como para comenzar a necesitar funciones bash.
SeldomNeedy
@SeldomNeedy Algunas de las cosas que destaca por defecto funcionan bien en cualquier shell POSIX. $(...)Es particularmente desagradable. Además, algunos de ellos son sutiles [ <(...)y cmd >& fileno reciben ningún resaltado de error, por ejemplo, simplemente no tienen resaltado especial para lo que quieren decir, con o sin g:is_bash]
Random832
@ Random832 Parece que ha habido alguna actividad sobre este tema recientemente en el rastreador de problemas de vim .
SeldomNeedy

Respuestas:

150

En breve:

  • Hay varios shells que implementan un superconjunto de la especificación POSIX sh . En diferentes sistemas, /bin/shpuede haber un enlace a ash, bash, dash, ksh, zsh, & c. (Sin embargo, siempre será compatible con sh, nunca csh o fish).

  • Siempre y cuando se limite a las shcaracterísticas solamente, puede (y probablemente incluso debería) usar #!/bin/shy el script debería funcionar bien, sin importar de qué shell se trate.

  • Si comienza a usar características específicas de bash (por ejemplo, matrices), debe solicitar específicamente bash, porque, incluso si /bin/shya invoca bash en su sistema, puede que no esté en el sistema de todos los demás , y su script no se ejecutará allí. (Lo mismo, por supuesto, se aplica a zsh y ksh). Puede usar shellcheck para identificar bashisms.

  • Incluso si el script es solo para uso personal, puede notar que algunos sistemas operativos cambian /bin/shdurante las actualizaciones, por ejemplo, en Debian solía ser bash, pero luego se reemplazó con un guión muy mínimo. Guiones que usaban bashismos pero que de #!/bin/shrepente se habían roto.

Sin embargo:

  • Incluso #!/bin/bashno es muy correcto. En diferentes sistemas, bash podría vivir en /usr/bino /usr/pkg/bino /usr/local/bin.

  • Una opción más confiable es #!/usr/bin/env bash, que usa $ PATH. (Aunque la envherramienta en sí tampoco está estrictamente garantizada, /usr/bin/envaún funciona en más sistemas que /bin/bash).

gravedad
fuente
10
Buena respuesta. ¿Querías hacerlo CW?
Mokubai
3
Solo una observación si bash se ejecuta desde /bin/shentonces, intentará imitar shcaracterísticas (ver página de manual ), no estoy seguro de que las características específicas de bash estén disponibles en este modo. envtool es una herramienta posix , debería encontrarse en la mayoría de las distribuciones, pero no estoy seguro, ya que algunos pueden no respetar posix.
Brice
8
No, de hecho, POSIX declara específicamente que no se puede suponer que está en /bin: " Las aplicaciones deben tener en cuenta que la RUTA estándar al shell no se puede suponer que sea / bin / sh o / usr / bin / sh, y se debe determinar mediante la interrogación de la RUTA devuelta por getconf RUTA, asegurando que la ruta devuelta sea una ruta absoluta y no un shell incorporado ". También vea esta pregunta y, específicamente, esta respuesta .
terdon
12
Hmm, bueno, ¿entonces eso significa que POSIX en realidad no define una forma portátil para que #!funcionen los scripts?
Grawity
44
POSIX sh no es Bourne: es más un derivado de ksh temprano que un descendiente directo de Bourne. Distinguirlos es fácil: echo foo ^ catemite foo ^ caten POSIX sh, y emite solo fooen Bourne (como ^es un carácter de tubería allí).
Charles Duffy
18

Use el shebang correspondiente al shell que realmente utilizó para desarrollar y depurar su script. Es decir, si su shell de inicio de sesión es bash, y ejecuta su script como ejecutable en su terminal, use #!/bin/bash. No asuma que las matrices ya que no se ha utilizado (o cualquier bashcaracterística que está consciente de), que está seguro de escoger cualquier cáscara te gusta. Hay muchas diferencias sutiles entre los shells ( echofunciones, bucles, lo que sea) que no se pueden descubrir sin una prueba adecuada.

Considere esto: si se va #!/bin/bashy sus usuarios no lo tienen, verán un mensaje de error claro, algo así como

Error: /bin/bash not found

La mayoría de los usuarios pueden arreglar esto en menos de un minuto instalando el paquete apropiado. Por otro lado, si reemplaza el shebang por #!/bin/shy lo prueba en un sistema donde /bin/shhay un enlace simbólico /bin/bash, sus usuarios que no lo tengan bashestarán en problemas. Lo más probable es que vean un mensaje de error críptico como:

Error in script.sh line 123: error parsing token xyz

Esto puede tomar horas para solucionarlo, y no habrá idea sobre qué shell deberían haber utilizado.

No hay muchas razones por las que quieras usar un shell diferente en el shebang. Una razón es cuando el caparazón que ha utilizado no está muy extendido. Otra es ganar rendimiento con el shcual es significativamente más rápido en algunos sistemas, Y su script será un cuello de botella de rendimiento. En ese caso, pruebe su script a fondo con el shell de destino, luego cambie el shebang.

Dmitry Grigoryev
fuente
@Hastur No entiendo tu comentario. El shebang definitivamente definirá qué shell se usará para ejecutar el script, ¿crees que mi respuesta implica lo contrario?
Dmitry Grigoryev
1
Olvídalo. No entendí la parte "por qué querrías usar" ...
Hastur
6

Solo deberías usarlo alguna vez #! /bin/sh.

No debe usar extensiones bash (o zsh, o fish, o ...) en un script de shell, nunca.

Solo debe escribir scripts de shell que funcionen con cualquier implementación del lenguaje de shell (incluidos todos los programas de "utilidad" que acompañan al propio shell). En estos días, probablemente pueda tomar POSIX.1-2001 ( no -2008) como autoridad para lo que el shell y las utilidades son capaces de hacer, pero tenga en cuenta que algún día puede ser llamado para portar su script a un sistema heredado (por ejemplo, Solaris o AIX) cuyo shell y utilidades fueron congelados alrededor de 1992

¿Qué, en serio?

Sí, en serio.

Aquí está la cosa: Shell es un lenguaje de programación terrible . Lo único que tiene es que /bin/shes el único intérprete de guiones que se garantiza que todas las instalaciones de Unix tengan.

Aquí está la otra cosa: /usr/bin/perles más probable que alguna iteración del intérprete principal de Perl 5 ( ) esté disponible en una instalación de Unix seleccionada al azar de lo que (/(usr|opt)(/(local|sfw|pkg)?)?/bin/bashestá. Otros buenos lenguajes de scripting (Python, Ruby, node.js, etc. - Incluso incluiré PHP y Tcl en esa categoría al comparar con shell) también están disponibles como bash y otros shells extendidos.

Por lo tanto, si tiene la opción de escribir un script bash, tiene la opción de usar un lenguaje de programación que no sea terrible.

Ahora, simples scripts de shell, del tipo que solo ejecuta algunos programas en una secuencia desde un trabajo cron o algo así, no hay nada de malo en dejarlos como scripts de shell. Pero los scripts de shell simples no necesitan matrices o funciones, ni [[siquiera. Y solo debe escribir scripts de shell complicados cuando no tiene otra opción. Las secuencias de comandos de Autoconf, por ejemplo, siguen siendo correctamente secuencias de comandos de shell. Pero esos scripts tienen que ejecutarse en cada encarnación /bin/shque sea relevante para el programa que se está configurando. y eso significa que no pueden usar ninguna extensión. Probablemente no tenga que preocuparse por los viejos Unix patentados en estos días, pero probablemente debería preocuparse por los BSD de código abierto actuales, algunos de los cuales no se instalanbashde forma predeterminada, y entornos integrados que le brindan solo un shell y un mínimo busybox.

En conclusión, en el momento en que te encuentras queriendo una característica que no está disponible en el lenguaje shell portátil, eso es una señal de que el script se ha vuelto demasiado complicado para seguir siendo un script shell. Reescríbalo en un mejor idioma.

zwol
fuente
44
Lo siento pero eso es un poco tonto. Sí, si está escribiendo algo que se i) distribuirá y ii) en diferentes entornos, debe apegarse a lo básico sh. Eso, sin embargo, está muy lejos de afirmar que nunca debes usar otros proyectiles. Hay miles (probablemente muchos más) scripts escritos todos los días y la gran mayoría de ellos solo se ejecutarán en una sola máquina. Su consejo tiene sentido en el código de producción, pero no para los trabajos diarios de "sysdaminy" para los que se escriben la mayoría de los scripts. Si sé que mi script solo será utilizado por mí y en una máquina Linux, bash está bien.
terdon
1
@terdon El punto es que si tienes la opción de escribir un script bash, entonces también tienes la opción de escribir un script perl (o lo que sea), y esta es siempre la mejor opción.
zwol
@terdon La respuesta seguramente no es tonta, su comentario es simplemente ingenuo. Los scripts de Shell son terribles de depurar en comparación con Perl y demás, para lo cual se pueden usar fácilmente IDE completos con depuración gráfica y todo. Además, la mayoría de los scripts escritos todos los días para hacer algo pequeño tienden a cambiarse y cambiarse de nuevo, crecer, se copian como ejemplos para colegas o la red, etc. Es muy probable que no haya ninguna razón para correr ese riesgo.
Thorsten Schöning
@ ThorstenSchöning dije un poco tonto. Si esto fuera Unix y Linux o incluso Stack Overflow , incluso podría estar de acuerdo. Si hablamos de mejores prácticas generales o programadores profesionales, por supuesto, es mejor atenerse a POSIX sh básico. Sin embargo, este es Super User , un sitio destinado a usuarios finales y decirle a la gente que nunca use bash o zsh cuando escribe scripts de shell es, en mi opinión, extremo.
terdon
@terdon En realidad, es más importante, en mi opinión, disuadir a los usuarios finales de que escriban scripts de shell complejos. Los profesionales tienen un nivel de habilidad más alto en promedio (por lo tanto, es más probable que sean capaces de hacer frente a las trampas de las secuencias de comandos complejas de shell), deben saber con cierta precisión a qué entornos necesitan ser portátiles y se les paga para no rendirse en la frustración.
zwol
4

En general, si el tiempo es más importante que la funcionalidad, usará el shell más rápido. sh a menudo tiene el alias de guión y tiende a usarse para tareas cron de root u operaciones por lotes donde cada (nano) segundo cuenta.

mckenzm
fuente
2
Cargar un intérprete de shell adicional desde el sistema de archivos puede negar tal ventaja, y cualquier cosa donde los nanosegundos (que están en el mismo orden de magnitud que un ciclo de máquina real) cuentan es el dominio de los programadores de lenguaje C y ensamblador.
rackandboneman
Los nanosegundos solo marcan la diferencia en los trabajos cron si tiene miles de millones de ellos, y cron no podrá manejar tantos de todos modos.
Dmitry Grigoryev
1
Incluso si mckenzm exageró un poco, ¡no se puede negar que tener más rendimiento es mejor! No te obsesiones con la parte "nano". (¡Los nanosegundos ni siquiera cuentan en C a menos que sea un bucle interno en un sistema en tiempo real o algo así!)
jpaugh
1
@jpaugh A menos que esté trabajando con un sistema (heredado) que asume que ciertas operaciones tomarán al menos un período específico de tiempo. ¡Sucede!
JAB
3

Para ser aún más breve, use shsi la portabilidad en la mayoría de los sistemas es lo más importante y bashsi desea usar algunas de sus características específicas, como los arreglos, si la versión de bash lo admite.

Spencer Williams
fuente
1
  • sh (para la mayoría de los casos), bash si se requiere específicamente (o ksh, o lo que sea)

El camino más seguro. cuando está codificado, sería / usr / bin / shellOfChoicepero una nueva convención que trato de usar siempre ahora, ya que las 'ubicaciones predeterminadas' a través de un cambio PATH pueden cambiar es:

#! / usr / bin / env sh o
#! / usr / bin / env bash
o, por ejemplo, scripts perl
#! / usr / bin / env perl -w

Por supuesto, cuando tiene una razón para que un script NUNCA tome una nueva RUTA automáticamente, continúe teniendo un código rígido, y luego / usr / bin / something debería ser la ruta más probable para usar.

Michael sintió
fuente