Ý nghĩa của dòng exec exec đuôi -n +3 $ 0 dòng trong tập tin 40_custom


17

Tôi đang cố gắng để hiểu các tập tin cấu hình grub. Vì vậy, trong quá trình này, tôi đã bắt gặp với tập tin /etc/grub.d/40_custom . Tập tin của tôi chứa các dòng sau:

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

vì hệ thống của tôi là boot kép và rõ ràng đây là bộ tải khởi động cho windows 10.

Câu hỏi của tôi mặc dù là phần này exec tail -n +3 $0.
Nếu tôi giải mã chính xác, điều này chỉ có nghĩa là in các dòng cuối cùng bắt đầu từ dòng thứ 3 ( +3) của tệp $0. $0tất nhiên trong trường hợp này là tập tin thực tế /etc/grub.d/40_custom .

Vậy, tại sao chúng ta sử dụng lệnh này trong tệp 40_custom ? Khi tôi nhận được nó, đầu ra sẽ giống nhau nếu ιt bị bỏ qua hoàn toàn. Sự khác biệt duy nhất tôi có thể nghĩ đến là dòng thứ 1 xác định trình thông dịch:

#!/bin/sh

Nhưng sau đó một lần nữa nó được thực hiện kể từ exec tail -n +3 $0sau nó. Vì vậy, đây chỉ là một quy ước (vô dụng)?

Câu trả lời:


16

Bí quyết là những gì execkhông:

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

Điều này có nghĩa là nó sẽ thay thế vỏ bằng bất cứ thứ gì được đưa ra exec, trong trường hợp này tail. Đây là một ví dụ về nó trong hành động:

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

$ ./foo.sh
echo foo

Vì vậy, echolệnh không được thực thi vì chúng tôi đã thay đổi shell và đang sử dụng tailthay thế. Nếu chúng tôi loại bỏ exec tail:

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

Vì vậy, đây là một thủ thuật gọn gàng cho phép bạn viết một kịch bản mà công việc duy nhất của nó là xuất nội dung của chính nó. Có lẽ, bất cứ cuộc gọi nào cũng 40_custommong đợi nội dung của nó là đầu ra. Tất nhiên, điều này đặt ra câu hỏi tại sao không chỉ chạy tail -n +3 /etc/grub.d/40_customtrực tiếp.

Tôi đoán câu trả lời là bởi vì grub sử dụng ngôn ngữ kịch bản riêng của nó và điều này làm cho nó cần cách giải quyết này.


Câu trả lời tốt đẹp! Nhưng nếu chúng ta viết #!/bin/tail -n +2như một shellbang thì sao? Nó sẽ in phần còn lại của tập tin?
val nói Phục hồi

@val hãy thử và xem :) Hóa ra nó không hoạt động hoàn hảo như một shebang, -n +2dường như được hiểu là -n 2và chỉ có hai dòng cuối cùng được in. Đó có lẽ là giá trị câu hỏi của riêng mình mặc dù.
terdon

3
@val ... hành vi của shebang ít được chuẩn hóa và di động hơn bạn mong đợi. Xem phần "giải thích các đối số lệnh" của en.wikipedia.org/wiki/Shebang_(Unix)#Portability
Charles Duffy

@val Điều đó hoạt động trong Linux nếu bạn loại bỏ khoảng trắng giữa n+. Trong Linux ít nhất, sau đường dẫn thực thi và khoảng trắng, các ký tự sau được coi là một đối số duy nhất. Vì vậy, với khoảng trắng, tail nhìn thấy nó và có thể quyết định loại bỏ bất kỳ -nđối số của tiền tố không hợp lệ nào (trong trường hợp này là khoảng trắng và a +) cho đến khi nó đạt được số. Tuy nhiên, như Charles Duffy đã nói, cách hiện tại họ làm điều này có lẽ dễ mang theo hơn đối với các Unice khác.
JoL

1
@fluffy Họ có thể sử dụng ở đây doc. Xem liên kết từ tài liệu Fedora trong câu trả lời của tôi. Họ sử dụng chính xác cat <<EOF ...EOFở đó
Sergiy Kolodyazhnyy

9

Thư mục /etc/grub.d/chứa nhiều tệp thực thi (điển hình là các kịch bản shell, nhưng bất kỳ loại thực thi nào khác cũng có thể). Bất cứ khi nào grub-mkconfigđược thực thi (ví dụ nếu bạn chạy update-grub, nhưng cả khi bạn cài đặt gói kernel đã cập nhật, thường có một hook sau cài đặt báo cho trình quản lý gói cập nhật grub.cfg), tất cả chúng đều được thực hiện theo thứ tự bảng chữ cái. Tất cả các kết quả đầu ra của chúng đều được nối và kết thúc trong tệp /boot/grub/grub.cfg, với các tiêu đề phần gọn gàng cho thấy phần nào đến từ /etc/grub.d/tệp nào.

Một tệp cụ thể 40_customnày được thiết kế để cho phép bạn dễ dàng thêm các mục / dòng vào grub.cfgbằng cách chỉ cần gõ / dán chúng vào tệp này. Các tập lệnh khác trong cùng thư mục thực hiện các tác vụ phức tạp hơn như tìm kiếm hạt nhân hoặc hệ điều hành không phải linux và tạo các mục menu cho chúng.

Để cho phép grub-mkconfigxử lý tất cả các tệp đó theo cùng một cách (thực thi và lấy đầu ra), 40_customlà một tập lệnh và sử dụng exec tail -n +3 $0cơ chế này để xuất nội dung của nó (trừ "tiêu đề"). Nếu nó không phải là một tệp thực thi, update-grubsẽ cần một ngoại lệ được mã hóa đặc biệt để lấy nội dung văn bản theo nghĩa đen của tệp này thay vì thực thi nó như tất cả các tệp khác. Nhưng nếu bạn (hoặc các nhà sản xuất phân phối linux) muốn đặt cho tệp này một tên khác thì sao? Hoặc nếu bạn không biết về ngoại lệ và tạo một tập lệnh shell có tên 40_customthì sao?

Bạn có thể đọc thêm về grub-mkconfig/etc/grub.d/*trong Hướng dẫn sử dụng GNU GRUB (mặc dù nó chủ yếu nói về các tùy chọn bạn có thể đặt /etc/default/grub) và cũng cần có một tệp /etc/grub.d/READMEnói rằng các tệp này được thực thi để tạo thành grub.cfg.


1
Trong khi câu trả lời của terdon giải thích cách thức hoạt động của dòng này, thì câu hỏi này đưa ra lý do thiết kế hợp lý hơn là tại sao GRUB lại làm mọi thứ theo cách này.
JoL

Câu trả lời chính xác. Theo quan điểm của bạn về các trường hợp ngoại lệ đặc biệt - một ngoại lệ "đơn giản hơn" có thể có hai thư mục, ví dụ như /etc/grub.d/execđối với các tệp / tập lệnh có thể xuất hiện và /etc/grub.d/staticđối với các tệp văn bản thuần túy hoặc một số chỉ báo khác để phân biệt các thư mục này. Tuy nhiên, đây là quyết định thiết kế mà họ đã đi cùng.
Stobor

3

TL; DR : đó là một mẹo để đơn giản hóa việc thêm các mục mới vào tệp

Toàn bộ điểm được mô tả trong một trong các trang Wiki của Ubuntu trên grub :

  1. Chỉ các tệp thực thi mới tạo đầu ra cho grub.cfg trong khi thực hiện cập nhật-grub.

Đầu ra của các tập lệnh /etc/grub.d/trở thành nội dung của grub.cfgtập tin.

Bây giờ, những gì execlàm gì? Nó có thể nối lại đầu ra cho toàn bộ tập lệnh hoặc nếu một lệnh được cung cấp - lệnh được đề cập sẽ vượt qua và thay thế quy trình tập lệnh. Những gì đã từng là kịch bản shell với PID 1234 bây giờ là taillệnh với PID 1234.

Bây giờ bạn đã biết rằng tail -n +3 $0in mọi thứ sau dòng thứ 3 trong chính tập lệnh. Vậy tại sao chúng ta cần phải làm điều này? Nếu grub chỉ quan tâm đến đầu ra, chúng ta cũng có thể làm như vậy

cat <<EOF
    menuentry {
    ...
    }
EOF

Trong thực tế, bạn sẽ tìm thấy cat <<EOFví dụ trong tài liệu của Fedora , mặc dù cho một mục đích khác. Toàn bộ vấn đề nằm ở phần bình luận - dễ sử dụng cho người dùng:

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

Với execmánh khóe, bạn không cần phải biết phải cat <<EOFlàm gì (spoiler, được gọi là here-doc ), cũng như bạn không phải nhớ thêm EOFdòng cuối cùng. Chỉ cần thêm menuentry vào tập tin và được thực hiện với nó. Ngoài ra, nếu bạn đang viết kịch bản thêm một menu, bạn chỉ cần thêm >>vào trong shell vào tệp này.

Xem thêm:


Nhưng tại sao không có grub đọc các tập tin trực tiếp? Bạn có biết tại sao họ chọn để thực hiện chúng thay thế? Sẽ đơn giản hơn nhiều khi có chúng dưới dạng các tệp văn bản đơn giản được đọc bởi bất kỳ quy trình nào tạo ra menu, nhưng thay vào đó, chúng chọn làm cho chúng có thể thực thi được và thêm vào cách giải quyết phức tạp này. Có phải vì grub có ngôn ngữ kịch bản riêng như tôi đặt ra trong câu trả lời của mình?
terdon

@terdon Tôi không nghĩ rằng điều này phải làm bất cứ điều gì với chính ngôn ngữ grub. Bạn sẽ không cần #!/bin/shtrong trường hợp đó. Tôi nghi ngờ đây chỉ đơn giản là lý do lịch sử (được chuyển từ cơ sở mã PUPA ) và một quyết định thiết kế lấy cảm hứng từ loại kịch bản SysV.
Sergiy Kolodyazhnyy

@terdon Điều này thực sự đã truyền cảm hứng cho một câu hỏi: unix.stackexchange.com/q/492966/85039
Sergiy Kolodyazhnyy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.