Значение строки «exec tail -n +3 $ 0» в файле 40_custom


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. Единственное отличие, о котором я могу подумать, это 1-я строка, которая идентифицирует переводчика:

#!/bin/sh

Но с другой стороны это выполняется, так как exec tail -n +3 $0следует за этим. Так это просто (бесполезное) соглашение?

Ответы:


16

Хитрость в том, что 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как ракушка? Будет ли печатать остальную часть файла?
говорит

@val попробуй и посмотри :) Оказывается, это не работает как шебанг, -n +2кажется, что это интерпретируется как -n 2и только две последние строки печатаются. Это, вероятно, стоит своего собственного вопроса, хотя.
Terdon

3
@val ... поведение Шебанга гораздо менее стандартизировано и переносимо, чем вы можете ожидать. См. Раздел «Интерпретация аргументов команды» en.wikipedia.org/wiki/Shebang_(Unix)#Portability
Чарльз Даффи,

@val Это работает в Linux, если вы удалите пробел между nи +. В Linux, по крайней мере, после пути к исполняемому файлу и пробела следующие символы рассматриваются как один аргумент. Таким образом, с пробелом tail видит его и, вероятно, решает удалить любой -nаргумент недопустимого префикса (в данном случае пробел и a +), пока не достигнет числа. Однако, как сказал Чарльз Даффи, нынешний способ, которым они это сделали, вероятно, более переносим для других Unices.
JoL

1
@fluffy Они могут использовать здесь док. Смотрите ссылку из документации Fedora в моем ответе. Они используют именно cat <<EOF ...EOFтам
Сергей Колодяжный

9

Каталог /etc/grub.d/содержит много исполняемых файлов (обычно это сценарии оболочки, но также возможен любой другой тип исполняемых файлов). Всякий раз, когда 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) захотите дать этому файлу другое имя? Или что, если вы не знали об исключении и создали сценарий оболочки с именем 40_custom?

Вы можете прочитать больше о grub-mkconfigи /etc/grub.d/*в GNU GRUB Manual (хотя он говорит в основном о параметрах , которые можно установить в /etc/default/grub), а также должна быть файл , /etc/grub.d/READMEкоторый гласит , что эти файлы будут выполнены с образованием grub.cfg.


1
В то время как ответ Тердона объясняет, как работает линия, этот дает более правдоподобную конструкторскую причину того, почему GRUB делает вещи таким образом.
JoL

Отличный ответ. На ваш взгляд, об особых исключениях - «более простое» исключение могло иметь две директории, например, /etc/grub.d/execдля исполняемых файлов / скриптов и /etc/grub.d/staticдля простых текстовых файлов, или какой-то другой индикатор, чтобы различать их. Однако это было дизайнерское решение, с которым они согласились.
Stobor

3

TL; DR : это хитрость, чтобы упростить добавление новых записей в файл

Весь смысл описан на одной из вики-страниц Ubuntu по grub :

  1. Только исполняемые файлы генерируют вывод в grub.cfg во время выполнения update-grub.

Вывод скриптов в /etc/grub.d/становится содержимым grub.cfgфайла.

Теперь, что делает exec? Он может либо повторно соединить вывод для всего сценария, либо, если указана команда, - упомянутая команда обгоняет и заменяет процесс сценария. То, что когда-то было сценарием оболочки с PID 1234, теперь является 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трюком, вы не должны знать, что делает cat <<EOF(спойлер, это называется here-doc ), и вы не должны добавлять EOFпоследнюю строку. Просто добавьте menuentry в файл и покончите с этим. Кроме того, если вы создаете сценарий, добавляя пункт меню, вы можете просто добавить через >>shell в этот файл.

Смотрите также:


Но почему бы grub не прочитать файлы напрямую? У вас есть идеи, почему они решили сделать их исполняемыми? Было бы намного проще иметь их в виде простых текстовых файлов, которые читаются любым процессом, генерирующим меню, но вместо этого они решили сделать их исполняемыми и добавили этот (аккуратный), но сложный обходной путь. Это потому, что у grub есть свой собственный язык сценариев, который я задаю в своем ответе?
Terdon

@terdon Я не думаю, что это связано с самим языком грубости. Вам не нужно #!/bin/shв этом случае. Я подозреваю, что это просто историческая причина (перенесенная из кодовой базы PUPA ) и дизайнерское решение, основанное на сценариях типа SysV.
Сергей Колодяжный

@terdon Это действительно вдохновило вопрос: unix.stackexchange.com/q/492966/85039
Сергей Колодяжный
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.