Signification de la ligne «exec tail -n +3 $ 0» dans le fichier 40_custom


17

J'essaie de comprendre les fichiers de configuration grub. Ainsi, pendant ce processus, je suis tombé sur le fichier /etc/grub.d/40_custom . Mon fichier contient les lignes suivantes:

#!/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
}

car mon système est à double démarrage et, apparemment, c'est le chargeur de démarrage pour Windows 10.

Ma question est cependant cette partie exec tail -n +3 $0.
Si je le déchiffre correctement, cela signifie simplement imprimer les dernières lignes à partir de la 3ème ligne ( +3) du fichier $0. $0bien sûr, dans ce cas, c'est le fichier réel /etc/grub.d/40_custom .

Alors, pourquoi utilisons-nous cette commande dans le fichier 40_custom ? Comme je l'obtiens, la sortie serait la même si ιt était complètement omis. La seule différence à laquelle je pourrais penser est la 1ère ligne qui identifie l'interprète:

#!/bin/sh

Mais là encore, il est exécuté depuis le exec tail -n +3 $0suit. Alors, est-ce juste une convention (inutile)?

Réponses:


16

L'astuce est ce qui execfait:

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

Cela signifie qu'il remplacera le shell par tout ce qui est donné execdans ce cas tail. En voici un exemple en action:

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

$ ./foo.sh
echo foo

La echocommande n'est donc pas exécutée car nous avons changé le shell et utilisons à la tailplace. Si nous supprimons exec tail:

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

Donc, c'est une astuce intéressante qui vous permet d'écrire un script dont le seul travail est de produire son propre contenu. Vraisemblablement, tout appel 40_customattend son contenu en sortie. Bien sûr, cela soulève la question de savoir pourquoi ne pas simplement exécuter tail -n +3 /etc/grub.d/40_customdirectement.

Je suppose que la réponse est parce qu'il grub utilise son propre langage de script , ce qui nécessite cette solution de contournement.


Bonne réponse! Mais que faire si nous écrivons #!/bin/tail -n +2comme un shellbang? Imprime-t-il le reste du fichier?
val dit Réintégrer Monica le

@val essayez-le et voyez :) Il s'avère que cela ne fonctionne pas parfaitement comme un shebang, le -n +2semble être interprété comme -n 2et seules les deux dernières lignes sont imprimées. Cela vaut probablement sa propre question.
terdon

3
Le comportement @val ... shebang est beaucoup moins standardisé et portable que vous ne le pensez. Voir la section "interprétation des arguments de commande" de en.wikipedia.org/wiki/Shebang_(Unix)#Portability
Charles Duffy

@val Cela fonctionne sous Linux si vous supprimez l'espace entre le net le +. Sous Linux au moins, après le chemin exécutable et un espace, les caractères suivants sont traités comme un seul argument. Ainsi, avec l'espace, la queue le voit et décide probablement de supprimer tout -nargument du préfixe invalide (dans ce cas, un espace et un +) jusqu'à ce qu'il atteigne le nombre. Cependant, comme Charles Duffy l'a dit, la façon dont ils l'ont fait est probablement plus portable pour les autres Unices.
JoL

1
@fluffy Ils peuvent utiliser ici le doc. Voir le lien de la documentation de Fedora dans ma réponse. Ils utilisent exactement cat <<EOF ...EOF
Sergiy Kolodyazhnyy

9

Le répertoire /etc/grub.d/contient de nombreux exécutables (généralement des scripts shell, mais tout autre type exécutable est également possible). Chaque fois qu'ils grub-mkconfigsont exécutés (par exemple, si vous exécutez update-grub, mais également lorsque vous installez un package de noyau mis à jour, qui a généralement un hook post-installation qui indique au gestionnaire de packages de mettre à jour grub.cfg), ils sont tous exécutés dans l'ordre alphabétique. Leurs sorties sont toutes concaténées et se retrouvent dans le fichier /boot/grub/grub.cfg, avec des en-têtes de section soignés qui montrent quelle partie provient de quel /etc/grub.d/fichier.

Ce fichier particulier 40_customest conçu pour vous permettre d'ajouter facilement des entrées / lignes en les grub.cfgtapant / collant simplement dans ce fichier. D'autres scripts dans le même répertoire effectuent des tâches plus complexes comme la recherche de noyaux ou de systèmes d'exploitation non linux et la création d'entrées de menu pour eux.

Afin de permettre grub-mkconfigde traiter tous ces fichiers de la même manière (exécuter et prendre la sortie), 40_customest un script et utilise ce exec tail -n +3 $0mécanisme pour sortir son contenu (moins l '"en-tête"). S'il ne s'agissait pas d'un exécutable, update-grubil faudrait une exception spéciale codée en dur pour prendre le contenu textuel littéral de ce fichier au lieu de l'exécuter comme tous les autres. Mais que se passe-t-il si vous (ou les fabricants d'une autre distribution Linux) souhaitez donner un nom différent à ce fichier? Ou si vous ne connaissiez pas l'exception et avez créé un script shell nommé 40_custom?

Vous pouvez en savoir plus sur grub-mkconfiget /etc/grub.d/*dans le manuel GNU GRUB (bien qu'il parle principalement des options que vous pouvez définir /etc/default/grub), et il devrait également y avoir un fichier /etc/grub.d/READMEindiquant que ces fichiers sont exécutés pour se former grub.cfg.


1
Alors que la réponse de terdon explique comment fonctionne la ligne, celle-ci donne une raison de conception plus plausible quant à la raison pour laquelle GRUB fait les choses de cette façon.
JoL

Très bonne réponse. Pour votre point sur les exceptions spéciales - une exception "plus simple" pourrait avoir eu deux répertoires, par exemple /etc/grub.d/execpour les fichiers / scripts exécutables, et /etc/grub.d/staticpour les fichiers de texte brut, ou un autre indicateur pour les distinguer. Cependant, c'était la décision de conception qu'ils ont prise.
Stobor

3

TL; DR : c'est une astuce pour simplifier l'ajout de nouvelles entrées au fichier

L'ensemble du point est décrit dans l'une des pages Wiki d'Ubuntu sur grub :

  1. Seuls les fichiers exécutables génèrent une sortie vers grub.cfg pendant l'exécution de update-grub.

La sortie des scripts dans /etc/grub.d/devient le contenu du grub.cfgfichier.

Maintenant, qu'est-ce que ça execfait? Il peut soit re-câbler la sortie pour le script entier ou si une commande est fournie - la commande mentionnée dépasse et remplace le processus de script. Ce qui était autrefois un script shell avec le PID 1234 est maintenant une tailcommande avec le PID 1234.

Maintenant, vous savez déjà que tail -n +3 $0tout imprime après la 3e ligne du script lui-même. Alors, pourquoi devons-nous faire cela? Si grub ne se soucie que de la sortie, nous pourrions tout aussi bien faire

cat <<EOF
    menuentry {
    ...
    }
EOF

En fait, vous trouverez un cat <<EOFexemple dans la documentation de Fedora , bien qu'à des fins différentes. Tout est dans les commentaires - facilité d'utilisation pour les utilisateurs:

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

Avec execastuce, vous n'avez pas besoin de savoir ce que cat <<EOFfait (spoiler, c'est ce qu'on appelle ici-doc ), ni vous devez vous rappeler d'ajouter le EOFsur la dernière ligne. Ajoutez simplement l'entrée de menu au fichier et terminez-le. De plus, si vous créez un script pour ajouter une entrée de menu, vous pouvez simplement ajouter via >>dans le shell à ce fichier.

Voir également:


Mais pourquoi ne pas demander à grub de lire les fichiers directement? Avez-vous une idée pourquoi ils ont choisi de les rendre exécutables à la place? Il serait beaucoup plus simple de les avoir sous forme de fichiers texte simples qui sont lus par le processus qui génère le menu, mais à la place, ils ont choisi de les rendre exécutables et ont ajouté cette solution de contournement (soignée) mais complexe. Est-ce parce que grub a son propre langage de script comme je le dis dans ma réponse?
terdon

@terdon Je ne pense pas que cela ait à voir avec le langage grub lui-même. Vous n'auriez pas besoin #!/bin/shdans ce cas. Je soupçonne que c'est simplement une raison historique (reprise de la base de code PUPA ) et une décision de conception inspirée du type de script SysV.
Sergiy Kolodyazhnyy

@terdon Cela a en fait inspiré une question: unix.stackexchange.com/q/492966/85039
Sergiy Kolodyazhnyy
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.