40_customファイルの「exec tail -n +3 $ 0」行の意味


17

grubの設定ファイルを理解しようとしています。そのため、このプロセス中にファイル/etc/grub.d/40_customに遭遇しました。私のファイルには次の行が含まれています。

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

私のシステムはデュアルブートであり、明らかにこれはWindows 10のブートローダーです。

私の質問はこの部分exec tail -n +3 $0です。
私がそれを正しく解読している場合、これ+3はファイルの3行目()から始まる最後の行を印刷することを意味します$0$0もちろんこの場合、実際のファイル/etc/grub.d/40_customです。

では、なぜ40_customファイルでこのコマンドを使用するのでしょうか?わかったように、ιtが完全に省略された場合、出力は同じになります。私が考えるかもしれない唯一の違いは、通訳を識別する最初の行です:

#!/bin/sh

しかし、その後、再び実行されexec tail -n +3 $0ます。それで、これは単なる(役に立たない)コンベンションですか?

回答:


16

その秘The execは次のとおりです。

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

これはexec、この場合、与えられたものでシェルを置き換えることを意味しますtail。動作中の例を次に示します。

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

$ ./foo.sh
echo foo

そのためecho、シェルを変更してtail代わりに使用しているため、コマンドは実行されません。を削除する場合exec tail

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

したがって、これは、独自のコンテンツを出力することのみを目的とするスクリプトを作成できる巧妙なトリックです。おそらく、どの呼び出しで40_customも、その内容が出力として期待されます。もちろん、これはなぜtail -n +3 /etc/grub.d/40_custom直接実行しないのかという疑問を招きます。

答えはgrub 、独自のスクリプト言語使用しているため、この回避策が必要だからだと思います。


いい答え!しかし#!/bin/tail -n +2、shellbangとして記述した場合はどうなりますか?ファイルの残りを印刷しますか?
valによると、モニカの復職

@valそれを試してみてください:)シバンとして完全に機能しないことが判明し、最後の2行だけが印刷さ-n +2れる-n 2と解釈されるようです。それはおそらくそれ自身の質問の価値があります。
テルドン

3
@val ... shebangの動作は、予想よりはるかに標準化されておらず、移植性に欠けています。セクション「コマンド引数の解釈」を参照してください。en.wikipedia.org/wiki/Shebang_(Unix)#Portability
チャールズ・ダフィー

@val nとの間のスペースを削除すると、Linuxで機能します+。少なくともLinuxでは、実行可能パスとスペースの後に、次の文字が単一の引数として扱われます。そのため、スペースでは、tailはそれを見て、おそらく無効なprefix -nの引数(この場合はスペースとa +)を番号に到達するまで削除することにします。しかし、Charles Duffyが言ったように、彼らがこれを行った現在の方法は、おそらく他のUnicesへの移植性が高いでしょう。
JoL

1
@fluffy こちらのドキュメント使用できます。私の答えのFedoraドキュメントからのリンクを参照してください。彼らはcat <<EOF ...EOFそこで正確に使用します
Sergiy Kolodyazhnyy

9

ディレクトリに/etc/grub.d/は多くの実行可能ファイルが含まれています(通常はシェルスクリプトですが、他の実行可能ファイルも使用できます)。grub-mkconfig実行されるたびに(たとえば、を実行する場合update-grub、通常はパッケージマネージャーに更新するようにインストール後のフックを設定する更新されたカーネルパッケージをインストールする場合もgrub.cfg)、それらはすべてアルファベット順に実行されます。それらの出力はすべて連結され、最終的/boot/grub/grub.cfg/etc/grub.d/fileになります。どの部分がどのファイルに由来するかを示すきちんとしたセクションヘッダーがあります。

この特定のファイル40_customgrub.cfg、このファイルに入力/貼り付けするだけで、簡単にエントリ/行を追加できるように設計されています。同じディレクトリ内の他のスクリプトは、カーネルやLinux以外のオペレーティングシステムを探し、それらのメニューエントリを作成するなど、より複雑なタスクを実行します。

grub-mkconfigこれらすべてのファイルを同じ方法で処理(実行して出力を取得)できるようにするため40_custom、スクリプトであり、このexec tail -n +3 $0メカニズムを使用してそのコンテンツを出力します(「ヘッダー」を除く)。実行可能ファイルではない場合update-grub、他のすべてのファイルのように実行するのではなく、このファイルのリテラルテキストコンテンツを取得するために、特別なハードコーディングされた例外が必要になります。しかし、あなた(または別のLinuxディストリビューションのメーカー)がこのファイルに別の名前を付けたい場合はどうでしょうか?または、例外について知らず、という名前のシェルスクリプトを作成した場合はどうなります40_customか?

あなたはより多くについて読むことができますgrub-mkconfigし、/etc/grub.d/*中にマニュアルGNU GRUB(それはあなたが設定できるオプションについて主に語ったが/etc/default/grub)、また、ファイルが存在すべきである/etc/grub.d/READMEこれらのファイルが実行され得ることを状態が形成していることgrub.cfg


1
terdonの答えはこのラインがどのように機能するかを説明しますが、これはGRUBがこのように物事を行う理由について、より妥当な設計上の理由を与えます。
JoL

素晴らしい答え。特別な例外については、「単純な」例外には/etc/grub.d/exec、実行可能ファイル/スクリプト/etc/grub.d/static用、プレーンテキストファイル用、またはこれらを区別する他のインジケータ用の2つのディレクトリがあります。しかし、これは彼らが行った設計上の決定でした。
ストボー

3

TL; DR:ファイルへの新しいエントリの追加を簡単にするのがコツ

ポイント全体は、grubに関するUbuntuのWikiページの 1つで説明されています

  1. update-grubの実行中に実行可能ファイルのみがgrub.cfgへの出力を生成します。

スクリプトの出力はファイル/etc/grub.d/の内容になりgrub.cfgます。

さて、何をしexecますか?スクリプト全体の出力を再配線するか、コマンドが提供されている場合、前述のコマンドがスクリプトプロセスを追い越して置き換えます。かつてPID 1234のシェルスクリプトだったのはtail、PID 1234のコマンドです。

これtail -n +3 $0で、スクリプト自体の3行目以降のすべてが出力されることが既にわかりました。では、なぜこれを行う必要があるのでしょうか?もしgrubが出力だけを気にするなら、同様にできる

cat <<EOF
    menuentry {
    ...
    }
EOF

実際、Fedoraのドキュメントにはcat <<EOF例がありますが、目的は異なります。全体のポイントはコメントにあります-ユーザーにとっての使いやすさ:

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

execトリックを使えば、何をするのかを知る必要はなくcat <<EOF(ネタバレ、here-docと呼ばれます)、EOF最後の行にを追加することを覚えておく必要もありません。menuentryをファイルに追加するだけで完了です。さらに、メニューエントリを追加するスクリプトを作成している場合は>>、シェルでこのファイルに単純に追加できます。

こちらもご覧ください:


しかし、grubがファイルを直接読み取らないのはなぜですか?代わりに実行可能にすることを選択した理由はありますか?メニューを生成するプロセスによって読み取られる単純なテキストファイルとして保存する方がはるかに簡単ですが、代わりに実行可能にすることを選択し、この(ニート)しかし複雑な回避策を追加しました。それは、答えの中で述べたように、grubが独自のスクリプト言語を持っているからでしょうか?
テルドン

@terdonこれはgrub言語自体とは何の関係もないと思います。#!/bin/shその場合は必要ありません。これは単に歴史的な理由(PUPAコードベースから引き継がれたもの)であり、SysVタイプのスクリプトに触発された設計上の決定だと思います。
セルギー・コロディアズニー

@terdonこれは実際に質問を引き起こしました
Sergiy Kolodyazhnyy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.