Demasiadas líneas de shebang (declaración de script): ¿alguna forma de reducir su cantidad?

12

Tengo un proyecto compuesto por unos 20 .sharchivos pequeños . Los denomino "pequeños" porque generalmente, ningún archivo tiene más de 20 líneas de código. Tomé un enfoque modular porque, por lo tanto, soy fiel a la filosofía de Unix y es más fácil para mí mantener el proyecto.

Al comienzo de cada .sharchivo, pongo #!/bin/bash.

En pocas palabras, entiendo que las declaraciones de script tienen dos propósitos:

  1. Ayudan al usuario a recordar qué shell se necesita para ejecutar el archivo (por ejemplo, después de algunos años sin usar el archivo).
  2. Se aseguran de que el script se ejecute solo con un determinado shell (Bash en ese caso) para evitar un comportamiento inesperado en caso de que se use otro shell.

Cuando un proyecto comienza a crecer de, digamos, 5 archivos a 20 archivos o de 20 a 50 archivos (no este caso, sino solo para demostrarlo) tenemos 20 líneas o 50 líneas de declaraciones de guiones. Admito que, aunque puede ser divertido para algunos, me parece un poco redundante usar 20 o 50 en lugar de decir solo 1 por proyecto (tal vez en el archivo principal del proyecto).

¿Hay alguna forma de evitar esta supuesta redundancia de 20 o 50 o un número mucho mayor de líneas de declaraciones de script utilizando alguna declaración de script "global", en algún archivo principal?

usuario9303970
fuente
2
No deberías pero podrías; quítelo y ejecute todos sus scripts con/bin/bash -x "$script"
jesse_b
... Por lo general, cuando algo llega a este punto, ya he concluido que es hora de dejar de usar scripts de shell y obtener un lenguaje de scripting real para hacer el trabajo.
Shadur

Respuestas:

19

Aunque su proyecto ahora puede consistir únicamente en 50 scripts de Bash, tarde o temprano comenzará a acumular scripts escritos en otros idiomas, como Perl o Python (por los beneficios que tienen estos lenguajes de scripts que Bash no tiene).

Sin una línea adecuada #!en cada secuencia de comandos, sería extremadamente difícil utilizar las diversas secuencias de comandos sin saber qué intérprete utilizar. No importa si cada script se ejecuta desde otros scripts , esto solo mueve la dificultad de los usuarios finales a los desarrolladores. Ninguno de estos dos grupos de personas debería necesitar saber en qué idioma se escribió un guión para poder usarlo.

Los scripts de Shell ejecutados sin una línea #!y sin un intérprete explícito se ejecutan de diferentes maneras dependiendo de qué shell los invoque (vea, por ejemplo, la pregunta ¿Qué intérprete de shell ejecuta un script sin shebang? Y especialmente la respuesta de Stéphane ), que no es lo que desea en un entorno de producción (desea un comportamiento consistente y posiblemente incluso portabilidad).

Los scripts ejecutados con un intérprete explícito serán ejecutados por ese intérprete independientemente de lo que #!diga la línea. Esto causará problemas más adelante si decide volver a implementar, por ejemplo, un script Bash en Python o en cualquier otro idioma.

Debes gastar esas teclas adicionales y siempre agregar una línea #!a cada script.


En algunos entornos, hay textos legales de múltiples párrafos en cada secuencia de comandos en cada proyecto. Sé muy feliz de que solo sea una línea #!que se sienta "redundante" en tu proyecto.

Kusalananda
fuente
La respuesta debería ampliarse de esta manera: el shell determina el intérprete por la #!línea tan pronto como el archivo tenga permiso ejecutable y no sea cargado por un intérprete, sino por el shell. Es decir, /path/to/myprog.plejecuta un programa perl si la #!línea está presente (apuntando al intérprete perl) y el archivo tiene permiso de ejecución.
rexkogitans
11

No entiendes el punto de #!. En realidad, es una directiva para el sistema operativo ejecutar este programa bajo el intérprete definido.

Entonces podría #!/usr/bin/perlexistir un script y el programa resultante puede ejecutarse como ./myprogramy podría usarse el intérprete perl. Similar #!/usr/bin/pythonharía que un programa se ejecute bajo python.

Entonces la línea #!/bin/bashle dice al sistema operativo que ejecute este programa en bash.

Aquí hay un ejemplo:

$ echo $0
/bin/ksh

$ cat x
#!/bin/bash

ps -aux | grep $$

$ ./x
sweh      2148  0.0  0.0   9516  1112 pts/5    S+   07:58   0:00 /bin/bash ./x
sweh      2150  0.0  0.0   9048   668 pts/5    S+   07:58   0:00 grep 2148

Entonces, a pesar de que mi shell es ksh, el programa "x" se ejecuta bajo bash debido a la #!línea, y podemos verlo explícitamente en la lista de procesos.

Stephen Harris
fuente
ps -auxpuede dar advertencia,
Weijun Zhou
@WeijunZhou Di más ...
Kusalananda
Alguna versión de psda la advertencia Warning: bad syntax, perhaps a bogus '-'?.
Weijun Zhou
2
pssuooprts la sintaxis de argumento BSD y UXIX . -auxestá mal UNIX Stephen probablemente significa auxcuál es el BSD correcto
Jasen
1
Gente, 2 cosas; (1) centrarse en el concepto de ejemplo (que psmuestra la llamada de bash) no la sintaxis explícita; (2) ps -auxfunciona perfectamente en CentOS 7 y Debian 9 sin advertencias; ese cut'n'paste fue exactamente el resultado de mi máquina; toda la discusión sobre si -es necesario o no es aplicable a versiones anteriores de Linux Procps, no a las versiones actuales.
Stephen Harris
9

En .sh

Lo que es malo es .shal final del nombre del archivo.

Imagine que reescribe uno de los scripts en python.

Bueno, ahora tiene que cambiar la primera línea para que #!/usr/bin/python3esto no sea tan malo, ya que también tuvo que cambiar cualquier otra línea de código en el archivo. Sin embargo, también debe cambiar el nombre del archivo de prog.sha prog.py. Y luego tiene que encontrar en todas partes en los otros scripts, y todos sus scripts de usuario (los que ni siquiera sabía que existían), y cambiarlos para usar el nuevo script. sed -e 's/.sh/.py/g'puede ayudar a los que conoces. Pero ahora su herramienta de control de revisiones muestra un cambio en muchos archivos no relacionados.

Alternativamente hacerlo de la manera Unix y el nombre del programa progno prog.sh.

En #!

Si tiene el correcto #!y configure el permiso / modo para incluir la ejecución. Entonces no necesita conocer al intérprete. La computadora lo hará por usted.

chmod +x prog #on gnu adds execute permission to prog.
./prog #runs the program.

Para sistemas que no son GNU, lea el manual para chmod(y aprenda octal), tal vez léalo de todos modos.

ctrl-alt-delor
fuente
1
Hmmm, las extensiones no son necesariamente malas, al menos ayudan a comunicar con otros usuarios cuál es el tipo de archivo.
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Pero no necesitas saber el idioma, solo necesitas ejecutarlo. Incluso Microsoft está tratando de alejarse de las extensiones (desafortunadamente lo están haciendo, las estoy ocultando). Sin embargo, estoy de acuerdo en que podrían ser una buena idea: .imagepara fotos. .audiopara sonido, etc. De este modo se le dice qué es pero no sobre la implementación / codificación.
ctrl-alt-delor
1
@ ctrl-alt-delor Si se llama al archivo launch-missileso delete-project-and-make-manager-pissedno quiero "simplemente ejecutarlo" cuando solo tenía curiosidad sobre el idioma :)
Sergiy Kolodyazhnyy
2
Si tienes curiosidad por el idioma, utiliza el filecomando. reconoce la mayoría de los tipos de archivos.
Jasen
2
Estoy de acuerdo en que las extensiones son malas para las secuencias de comandos ejecutables, exactamente por las razones indicadas. Uso una extensión .sh para una secuencia de comandos que debe ser obtenida por otra secuencia de comandos de shell (es decir, una secuencia de comandos no ejecutable): en ese escenario, el sufijo es importante / válido porque nos dice cómo podemos usar el archivo.
Laurence Renshaw
8

[Las líneas hashbang] aseguran que el script se ejecute solo con un determinado shell (Bash en ese caso) para evitar un comportamiento inesperado en caso de que se use otro shell.

Probablemente valga la pena señalar que esto está bastante mal. La línea hashbang de ninguna manera le impide intentar alimentar el archivo a un intérprete / intérprete incorrecto:

$ cat array.sh
#!/bin/bash
a=(a b c)
echo ${a[1]} $BASH_VERSION
$ dash array.sh
array.sh: 2: array.sh: Syntax error: "(" unexpected

(o de manera similar con awk -f array.shetc.)


Pero, en cualquier caso, otro enfoque de la modularidad sería definir funciones de shell en los archivos, una función por archivo, si lo desea, y luego sourcelos archivos del programa principal. De esa manera, no necesitarías hashbangs: serían tratados como cualquier otro comentario, y todo se ejecutaría usando el mismo shell. La desventaja sería que necesitaría sourcelos archivos con las funciones (por ejemplo for x in functions/*.src ; do source "$x" ; done), y todos se ejecutarían con el mismo shell, por lo que no podría reemplazar directamente uno de ellos con una implementación de Perl.

ilkkachu
fuente
+1 por ejemplo con matrices bash. Los usuarios que no estén familiarizados con múltiples shells o algo de lo que POSIX define, pueden asumir que algunas características de un shell existen en otro. También para funciones, esto puede ser relevante: unix.stackexchange.com/q/313256/85039
Sergiy Kolodyazhnyy
4

¿Hay alguna forma de evitar esta supuesta redundancia de 20 o 50 o un número mucho mayor de líneas de declaraciones de script utilizando alguna declaración de script "global", en algún archivo principal?

Hay - se llama portabilidad o cumplimiento POSIX. Esfuércese por escribir siempre scripts de manera portátil y neutral para el shell, aprovéchelos solo de un script que use #!/bin/sho cuando lo use de forma /bin/shinteractiva (o al menos un shell compatible con POSIX como ksh).

Sin embargo, los scripts portátiles no lo salvan de:

  • necesidad de usar características de uno de los proyectiles ( la respuesta de ilkkachu es un ejemplo)
  • necesidad de usar lenguajes de script más avanzados que shells (Python y Perl)
  • Error de PEBKAC: su script cae en manos del usuario J.Doe que ejecuta su script no como usted lo pretendía, por ejemplo, ejecutan /bin/shscript csh.

Si puedo expresar mi opinión, "evitar la redundancia" eliminando #!es un objetivo equivocado. El objetivo debe ser un rendimiento consistente y, en realidad, un script de trabajo que funcione, y considere casos de esquina, siga ciertas reglas generales como no-parsing-ls o siempre-citando variables-a menos que se necesite dividir palabras .

Ayudan al usuario a recordar qué shell se necesita para ejecutar el archivo (por ejemplo, después de algunos años sin usar el archivo).

Realmente no son para el usuario: son para que el sistema operativo ejecute un intérprete adecuado. "Apropiado" en este caso, significa que OS encuentra lo que está en shebang; si el código debajo es incorrecto, es culpa del autor. En cuanto a la memoria del usuario, no creo que el "usuario" de programas como Microsoft Word o Google Chrome haya necesitado saber en qué idioma están escritos los programas; Los autores pueden necesitar.

Por otra parte, los scripts escritos de forma portátil pueden eliminar la necesidad de recordar incluso qué shell usó originalmente, porque los scripts portátiles deberían funcionar en cualquier shell compatible con POSIX.

Se aseguran de que el script se ejecute solo con un determinado shell (Bash en ese caso) para evitar un comportamiento inesperado en caso de que se use otro shell.

Ellos no. Lo que realmente evita un comportamiento inesperado es escribir scripts portátiles.

Sergiy Kolodyazhnyy
fuente
1

La razón por la que necesita colocar #!/bin/bashen la parte superior de cada script es que lo está ejecutando como un proceso separado.

Cuando ejecuta un script como un nuevo proceso, el sistema operativo ve que es un archivo de texto (en lugar de un ejecutable binario) y necesita saber qué intérprete ejecutar. Si no lo dice, el sistema operativo solo puede adivinar, utilizando su configuración predeterminada (que un administrador puede cambiar). Entonces, la #!línea (además de agregar permisos ejecutables, por supuesto) convierte un archivo de texto simple en un ejecutable que se puede ejecutar directamente, con la confianza de que el sistema operativo sabe qué hacer con él.

Si desea eliminar la #!línea, sus opciones son:

  1. Ejecute el script directamente (como lo está haciendo ahora) y espere que el intérprete predeterminado coincida con el script: probablemente esté seguro en la mayoría de los sistemas Linux, pero no sea portátil a otros sistemas (por ejemplo, Solaris), y se puede cambiar a cualquier cosa (¡incluso podría configurarlo para que se ejecute vimen el archivo!).

  2. Ejecute el script usando bash myscript.sh. Esto funciona bien, pero debe especificar la ruta al script, y es desordenado.

  3. Obtenga el script utilizando . myscript.sh, que ejecuta el script dentro del proceso actual de interpretación (es decir, no como un subproceso). Pero esto seguramente requerirá modificaciones a sus scripts, y los hace menos modulares / reutilizables.

En los casos 2 y 3, el archivo no necesita (y no debería tener) permisos de ejecución, y darle un sufijo .sh(o .bash) es una buena idea.

Pero ninguna de esas opciones se ve bien para su proyecto, ya que dijo que desea mantener sus scripts modulares (=> independientes y autónomos), por lo que debe mantener su #!/bin/bashlínea en la parte superior de los scripts.

En su pregunta, también dijo que sigue la filosofía de Unix. Si eso es cierto, entonces debes aprender para qué #!es realmente la línea, ¡y seguir usándola en cada script ejecutable!

Laurence Renshaw
fuente
Gracias por la buena respuesta. Me encantó todo en la respuesta y pensé en Upvoting hasta que esto: then you should learn what the #! line is really for, and keep using it in every executable script!. Uno puede seguir la filosofía de los Estados Unidos hasta cierto punto de conocimiento. Después de todas estas respuestas, entendí por qué se puede incluir en ese término. Sugiero editar la respuesta, reemplazándola con "Vea el shebang como una parte interna de la Filosofía Unix que respeta y adopta".
user9303970
1
Lo siento si fui directo, o si ese comentario me pareció grosero. Sus comentarios sugirieron que prefiere trabajar en un IDE, con un 'proyecto' que le permite definir reglas globales para los scripts dentro de ese proyecto. Esa es una forma válida de desarrollo, pero no encaja bien con el tratamiento de cada script como un programa independiente e independiente (la filosofía de Unix a la que se refirió).
Laurence Renshaw