我正在尝试了解 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_自定义文件?据我所知,如果完全省略ιt,输出将是相同的。我可能想到的唯一不同是第一行,它标识了解释器:
#!/bin/sh
但是由于遵循了它,因此它又被执行了exec tail -n +3 $0
。那么,这只是一个(无用的)惯例吗?
答案1
诀窍在于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.
这意味着它将用给定的任何东西替换 shell exec
,在本例中是tail
。下面是它的实际示例:
$ cat ~/foo.sh
#!/bin/sh
exec tail -n +3 "$0"
echo foo
$ ./foo.sh
echo foo
因此,由于我们更改了 shell 并使用了其他命令,因此该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
使用自己的脚本语言这使得它需要这个解决方法。
答案2
该目录/etc/grub.d/
包含许多可执行文件(通常是 shell 脚本,但其他可执行文件类型也是可能的)。每当grub-mkconfig
执行时(例如,如果你运行update-grub
,还有当你安装更新的内核包时,通常有一个安装后钩子告诉包管理器更新grub.cfg
),它们都按字母顺序执行。 它们的输出全部连接起来,最终放在文件 中/boot/grub/grub.cfg
,并带有整齐的节标题,显示哪个部分来自哪个/etc/grub.d/
文件。
此特定文件40_custom
旨在让您grub.cfg
通过简单地键入/粘贴到此文件中来轻松添加条目/行。同一目录中的其他脚本执行更复杂的任务,如查找内核或非 Linux 操作系统并为其创建菜单条目。
为了允许grub-mkconfig
以相同的方式处理所有这些文件(执行并获取输出),40_custom
是一个脚本并使用此exec tail -n +3 $0
机制输出其内容(减去“标题”)。如果它不是可执行文件,update-grub
则需要一个特殊的硬编码异常来获取此文件的文字内容,而不是像所有其他文件一样执行它。但是,如果您(或其他 Linux 发行版的制造商)想给这个文件起一个不同的名字,该怎么办?或者,如果您不知道这个异常并创建了一个名为的 shell 脚本,该怎么办40_custom
?
您可以grub-mkconfig
在/etc/grub.d/*
GNU GRUB 手册(尽管它主要谈论您可以在其中设置的选项/etc/default/grub
),并且还应该有一个文件/etc/grub.d/README
说明这些文件被执行以形成grub.cfg
。
答案3
总结:这是简化向文件添加新条目的一个技巧
整个要点在其中一篇中进行了描述Ubuntu 上有关 grub 的 Wiki 页面:
- 只有可执行文件在执行 update-grub 期间才会生成 grub.cfg 的输出。
脚本的输出/etc/grub.d/
成为文件的内容grub.cfg
。
现在,做什么exec
?它可以重新连接整个脚本的输出,或者如果提供了命令,则所提及的命令将接管并替换脚本进程。曾经是 PID 为 1234 的 shell 脚本现在是tail
PID 为 1234 的命令。
现在你已经知道了,它tail -n +3 $0
会打印脚本本身第 3 行之后的所有内容。那么我们为什么需要这样做呢?如果 grub 只关心输出,我们也可以这样做
cat <<EOF
menuentry {
...
}
EOF
事实上,你会发现cat <<EOF
例子Fedora 文档,尽管目的不同。重点在于评论 - 方便用户使用:
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment.
使用exec
trick,你不需要知道它做什么cat <<EOF
(剧透,这叫做此处文档),也不必记得EOF
在最后一行添加。只需将菜单项添加到文件即可。另外,如果您正在编写添加菜单项的脚本,只需将>>
shell 中的 via 附加到此文件即可。