Significato della riga "exec tail -n +3 $ 0" nel file 40_custom


17

Sto cercando di capire i file di configurazione di grub. Quindi, durante questo processo mi sono imbattuto nel file /etc/grub.d/40_custom . Il mio file contiene le seguenti righe:

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

dato che il mio sistema è dual boot e apparentemente questo è il boot loader per Windows 10.

La mia domanda però è questa parte exec tail -n +3 $0.
Se lo sto decifrando correttamente, ciò significa solo stampare le ultime righe a partire dalla terza riga ( +3) del file $0. $0ovviamente in questo caso è il file attuale /etc/grub.d/40_custom .

Quindi, perché utilizziamo questo comando nel file 40_custom ? A quanto pare, l'output sarebbe lo stesso se ιt fosse omesso del tutto. L'unica diversa che mi viene in mente è la prima riga che identifica l'interprete:

#!/bin/sh

Ma poi di nuovo viene eseguito poiché lo exec tail -n +3 $0segue. Quindi, questa è solo una convenzione (inutile)?

Risposte:


16

Il trucco è cosa execfa:

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

Ciò significa che sostituirà la shell con tutto ciò che viene dato exec, in questo caso tail. Ecco un esempio in azione:

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

$ ./foo.sh
echo foo

Quindi il echocomando non viene eseguito poiché abbiamo cambiato la shell e stiamo usando tailinvece. Se rimuoviamo il exec tail:

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

Quindi, questo è un trucco che ti consente di scrivere uno script il cui unico compito è quello di produrre i suoi contenuti. Presumibilmente, qualunque chiamata si 40_customaspetti il ​​suo contenuto come output. Naturalmente, questo pone la domanda sul perché non semplicemente correre tail -n +3 /etc/grub.d/40_customdirettamente.

Immagino che la risposta sia perché grub utilizza il proprio linguaggio di scripting e questo rende necessaria questa soluzione alternativa.


Bella risposta! E se scrivessimo #!/bin/tail -n +2come shellbang? Stampa il resto del file?
Val dice Reintegrare Monica il

@val provalo e vedi :) Si scopre che non funziona perfettamente come uno shebang, -n +2sembra essere interpretato come -n 2e sono stampate solo le ultime due righe. Questo probabilmente vale la sua domanda però.
terdon,

3
@val ... il comportamento di shebang è molto meno standardizzato e portatile di quanto ci si possa aspettare. Vedi la sezione "interpretazione degli argomenti dei comandi" di en.wikipedia.org/wiki/Shebang_(Unix)#Portability
Charles Duffy

@val Funziona in Linux se si rimuove lo spazio tra il ne il +. Almeno in Linux, dopo il percorso eseguibile e uno spazio, i seguenti caratteri vengono trattati come un singolo argomento. Quindi, con lo spazio, tail lo vede e probabilmente decide di rimuovere qualunque -nargomento del prefisso non valido abbia (in questo caso, uno spazio e una +) fino a quando non arriva al numero. Tuttavia, come ha detto Charles Duffy, il modo in cui lo hanno fatto è probabilmente più portatile per gli altri Unices.
JoL

1
@fluffy Essi possono utilizzare qui doc. Vedi il link dalla documentazione di Fedora nella mia risposta. Usano esattamente cat <<EOF ...EOF
Sergiy Kolodyazhnyy

9

La directory /etc/grub.d/contiene molti eseguibili (in genere script di shell, ma è possibile anche qualsiasi altro tipo di eseguibile). Ogni volta che grub-mkconfigviene eseguito (ad esempio se si esegue update-grub, ma anche quando si installa un pacchetto kernel aggiornato, che di solito ha un gancio post-installazione che dice al gestore pacchetti di aggiornare grub.cfg), sono tutti eseguiti in ordine alfabetico. Tutti i loro output vengono concatenati e finiscono nel file /boot/grub/grub.cfg, con intestazioni di sezione ordinate che mostrano quale parte proviene da quale /etc/grub.d/file.

Questo particolare file 40_customè progettato per consentire di aggiungere facilmente voci / righe grub.cfgsemplicemente digitandole / incollandole in questo file. Altri script nella stessa directory svolgono compiti più complessi come la ricerca di kernel o sistemi operativi non Linux e la creazione di voci di menu per essi.

Al fine di consentire grub-mkconfigdi trattare tutti quei file nello stesso modo (esegui e accetta l'output), 40_customè uno script e usa questo exec tail -n +3 $0meccanismo per produrre il suo contenuto (meno la "header"). Se non fosse un eseguibile, update-grubsarebbe necessaria una speciale eccezione hardcoded per prendere il contenuto letterale di questo file invece di eseguirlo come tutti gli altri. Ma cosa succede se tu (o i produttori di un'altra distribuzione di Linux) volete dare a questo file un nome diverso? O se non sapessi dell'eccezione e creassi uno script shell chiamato 40_custom?

Puoi leggere di più grub-mkconfige /etc/grub.d/*nel Manuale di GNU GRUB (anche se parla principalmente delle opzioni che puoi impostare /etc/default/grub), e dovrebbe esserci anche un file /etc/grub.d/READMEche afferma che questi file vengono eseguiti nel modulo grub.cfg.


1
Mentre la risposta di Terdon spiega come funziona la linea, questa fornisce una ragione di progettazione più plausibile per cui GRUB fa le cose in questo modo.
JoL

Bella risposta. Per quanto riguarda le eccezioni speciali, un'eccezione "più semplice" avrebbe potuto avere due directory, ad esempio /etc/grub.d/execper file / script descrittivi e /etc/grub.d/staticper file di testo semplice o qualche altro indicatore per distinguerli. Tuttavia, questa è stata la decisione di progettazione che hanno seguito.
Stobor

3

TL; DR : è un trucco per semplificare l'aggiunta di nuove voci al file

L'intero punto è descritto in una delle pagine Wiki di Ubuntu su grub :

  1. Solo i file eseguibili generano output su grub.cfg durante l'esecuzione di update-grub.

L'output degli script in /etc/grub.d/diventa il contenuto del grub.cfgfile.

Ora cosa fa exec? Può ricollegare l'output per l'intero script o se viene fornito un comando: il comando citato supera e sostituisce il processo di script. Quello che una volta era lo script shell con PID 1234 ora è il tailcomando con PID 1234.

Ora sai già che tail -n +3 $0stampa tutto dopo la terza riga nello script stesso. Quindi perché dobbiamo farlo? Se grub si preoccupa solo dell'output, potremmo fare altrettanto

cat <<EOF
    menuentry {
    ...
    }
EOF

In effetti, troverai degli cat <<EOFesempi nella documentazione di Fedora , anche se per uno scopo diverso. Il punto è nei commenti: facilità d'uso per gli utenti:

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

Con il exectrucco, non devi sapere cosa fa cat <<EOF(spoiler, che si chiama here-doc ), né devi ricordare di aggiungere EOFil nell'ultima riga. Basta aggiungere menuentry al file e finirlo. Inoltre, se stai scripting aggiungendo un menuentry, puoi semplicemente aggiungere via >>in shell a questo file.

Guarda anche:


Ma perché non far leggere direttamente i file a grub? Hai idea del perché hanno scelto di renderli eseguibili invece? Sarebbe molto più semplice averli come semplici file di testo che vengono letti da qualunque processo generi il menu, ma invece hanno scelto di renderli eseguibili e hanno aggiunto questa soluzione (ordinata) ma complessa. È perché grub ha il suo linguaggio di scripting mentre io sostengo nella mia risposta?
terdon,

@terdon Non penso che questo abbia a che fare con il linguaggio stesso di grub. Non avresti bisogno #!/bin/shin quel caso. Sospetto che questo sia semplicemente un motivo storico (riportato dalla base di codice PUPA ) e una decisione progettuale ispirata al tipo di scripting SysV.
Sergiy Kolodyazhnyy,

@terdon Questo in realtà ha ispirato una domanda: unix.stackexchange.com/q/492966/85039
Sergiy Kolodyazhnyy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.