Significado de la línea "exec tail -n +3 $ 0" en el archivo 40_custom


17

Estoy tratando de entender los archivos de configuración de grub. Entonces, durante este proceso, me encontré con el archivo /etc/grub.d/40_custom . Mi archivo contiene las siguientes líneas:

#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.
menuentry "Windows 10" --class windows --class os {
insmod part_msdos
savedefault
insmod ntfs
insmod ntldr
set root='(hd0,msdos1)'
ntldr ($root)/bootmgr
}

Como mi sistema es de arranque dual y aparentemente este es el gestor de arranque para Windows 10.

Sin embargo, mi pregunta es esta parte exec tail -n +3 $0.
Si lo estoy descifrando correctamente, esto solo significa imprimir las últimas líneas a partir de la tercera línea ( +3) del archivo $0. $0Por supuesto, en este caso es el archivo real /etc/grub.d/40_custom .

Entonces, ¿por qué usamos este comando en el archivo 40_custom ? A medida que lo entiendo, la salida sería la misma si se omitiera por completo. Lo único diferente en lo que podría pensar es en la primera línea que identifica al intérprete:

#!/bin/sh

Pero, de nuevo, se ejecuta ya que lo exec tail -n +3 $0sigue. Entonces, ¿es solo una convención (inútil)?

Respuestas:


16

El truco es lo que exechace:

$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

    Execute COMMAND, replacing this shell with the specified program.
    ARGUMENTS become the arguments to COMMAND.  If COMMAND is not specified,
    any redirections take effect in the current shell.

Esto significa que reemplazará el shell con lo que se le dé exec, en este caso tail. Aquí hay un ejemplo de ello en acción:

$ cat ~/foo.sh
#!/bin/sh
exec tail -n +3 "$0"
echo foo

$ ./foo.sh
echo foo

Por lo tanto, el echocomando no se ejecuta ya que hemos cambiado el shell y estamos usando en su taillugar. Si eliminamos el exec tail:

$ cat ~/foo.sh
#!/bin/sh
echo foo
$ ./foo.sh
foo

Por lo tanto, este es un buen truco que le permite escribir un script cuyo único trabajo es generar sus propios contenidos. Presumiblemente, cualquier llamada 40_customespera su contenido como salida. Por supuesto, esto plantea la pregunta de por qué no solo se ejecuta tail -n +3 /etc/grub.d/40_customdirectamente.

Supongo que la respuesta es porque grub usa su propio lenguaje de secuencias de comandos y esto hace que necesite esta solución.


¡Buena respuesta! Pero, ¿y si escribimos #!/bin/tail -n +2como un shellbang? ¿Imprimirá el resto del archivo?
Val dice Reinstate Monica

@val pruébalo y ve :) Resulta que no funciona perfectamente como un shebang, -n +2parece interpretarse como -n 2y solo se imprimen las dos últimas líneas. Sin embargo, eso probablemente valga su propia pregunta.
terdon el

3
@val ... el comportamiento shebang es mucho menos estandarizado y portátil de lo que cabría esperar. Vea la sección "interpretación de los argumentos del comando" en en.wikipedia.org/wiki/Shebang_(Unix)#Portability
Charles Duffy

@val Eso funciona en Linux si elimina el espacio entre el ny el +. Al menos en Linux, después de la ruta ejecutable y un espacio, los siguientes caracteres se tratan como un argumento único. Entonces, con el espacio, tail lo ve y probablemente decide eliminar cualquier -nargumento de prefijo no válido (en este caso, un espacio y a +) hasta que llegue al número. Sin embargo, como dijo Charles Duffy, la forma actual en que lo hicieron es probablemente más portátil para otros Unices.
JoL

1
@fluffy Se pueden utilizar aquí doc. Vea el enlace de la documentación de Fedora en mi respuesta. Usan exactamente cat <<EOF ...EOFallí
Sergiy Kolodyazhnyy

9

El directorio /etc/grub.d/contiene muchos ejecutables (generalmente scripts de shell, pero también es posible cualquier otro tipo de ejecutable). Siempre que grub-mkconfigse ejecuta (por ejemplo, si ejecuta update-grub, pero también cuando instala un paquete de kernel actualizado, que generalmente tiene un enlace posterior a la instalación que le indica al administrador del paquete que actualice grub.cfg), todos se ejecutan en orden alfabético. Todos sus resultados se concatenan y terminan en el archivo /boot/grub/grub.cfg, con encabezados de sección que muestran qué parte proviene de qué /etc/grub.d/archivo.

Este archivo en particular 40_customestá diseñado para permitirle agregar fácilmente entradas / líneas grub.cfgsimplemente escribiéndolas / pegándolas en este archivo. Otros scripts en el mismo directorio realizan tareas más complejas, como buscar núcleos o sistemas operativos que no sean Linux y crear entradas de menú para ellos.

Para permitir grub-mkconfigtratar todos esos archivos de la misma manera (ejecutar y tomar la salida), 40_customes un script y utiliza este exec tail -n +3 $0mecanismo para generar su contenido (menos el "encabezado"). Si no fuera un ejecutable, update-grubnecesitaría una excepción especial codificada para tomar el contenido de texto literal de este archivo en lugar de ejecutarlo como todos los demás. Pero entonces, ¿qué pasa si usted (o los creadores de otra distribución de Linux) quiere darle a este archivo un nombre diferente? ¿O qué pasa si no sabía sobre la excepción y creó un script de shell llamado 40_custom?

Puede leer más sobre grub-mkconfigy /etc/grub.d/*en el Manual de GNU GRUB (aunque habla principalmente de las opciones que puede configurar /etc/default/grub), y también debe haber un archivo /etc/grub.d/READMEque indique que estos archivos se ejecutan para formarse grub.cfg.


1
Si bien la respuesta de terdon explica cómo funciona la línea, esta da una razón de diseño más plausible de por qué GRUB hace las cosas de esta manera.
JoL

Gran respuesta. Según su punto de vista sobre excepciones especiales, una excepción "más simple" podría haber sido tener dos directorios, por ejemplo, /etc/grub.d/execpara archivos / scripts ejecutables, y /etc/grub.d/staticpara archivos de texto sin formato, o algún otro indicador para distinguirlos. Sin embargo, esta fue la decisión de diseño que tomaron.
Stobor

3

TL; DR : es un truco para simplificar la adición de nuevas entradas al archivo

Todo el punto se describe en una de las páginas Wiki de Ubuntu en grub :

  1. Solo los archivos ejecutables generan resultados en grub.cfg durante la ejecución de update-grub.

La salida de scripts en se /etc/grub.d/convierte en el contenido del grub.cfgarchivo.

¿Ahora que hace exec? Puede volver a cablear la salida para el script completo o si se proporciona un comando: el comando mencionado supera y reemplaza el proceso del script. Lo que una vez fue script de shell con PID 1234 ahora es tailcomando con PID 1234.

Ahora ya sabe que tail -n +3 $0imprime todo después de la tercera línea en el script en sí. Entonces, ¿por qué necesitamos hacer esto? Si grub solo se preocupa por la salida, también podríamos hacerlo

cat <<EOF
    menuentry {
    ...
    }
EOF

De hecho, encontrará cat <<EOFejemplos en la documentación de Fedora , aunque para un propósito diferente. El punto principal está en los comentarios: facilidad de uso para los usuarios:

# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.

Con exectruco, no tienes que saber qué hace cat <<EOF(spoiler, eso se llama here-doc ), ni debes recordar agregar el EOFen la última línea. Simplemente agregue menuentry al archivo y termine con él. Además, si está creando una secuencia de comandos para agregar un menú, simplemente puede agregarlo >>en shell a este archivo.

Ver también:


Pero ¿por qué no hacer que grub lea los archivos directamente? ¿Tienes alguna idea de por qué eligieron hacerlos ejecutables en su lugar? Sería mucho más simple tenerlos como archivos de texto simples que son leídos por cualquier proceso que genere el menú, pero en su lugar optaron por hacerlos ejecutables y agregaron esto (ordenado) pero una solución compleja. ¿Es porque grub tiene su propio lenguaje de scripting como lo postulo en mi respuesta?
terdon el

@terdon No creo que esto tenga que ver con el lenguaje grub en sí. No necesitarías #!/bin/shen ese caso. Sospecho que esto es simplemente una razón histórica (transferida de la base de código PUPA ) y una decisión de diseño inspirada en el tipo de scripting SysV.
Sergiy Kolodyazhnyy

@terdon Esto realmente inspiró una pregunta: unix.stackexchange.com/q/492966/85039
Sergiy Kolodyazhnyy
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.