ความหมายของบรรทัด“ exec tail -n +3 $ 0” ในไฟล์ 40_custom


17

ฉันกำลังพยายามทำความเข้าใจไฟล์กำหนดค่าด้วง ดังนั้นในระหว่างกระบวนการนี้ฉันมาข้ามกับแฟ้ม/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 เมื่อฉันได้รับมันเอาท์พุทจะเหมือนกันถ้ามันถูกละไว้โดยสิ้นเชิง ข้อแตกต่างที่ฉันอาจนึกถึงคือบรรทัดที่ 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เป็น shellbang ล่ะ? มันจะพิมพ์ไฟล์ที่เหลือหรือไม่?
วาลพูดว่า Reinstate Monica

@ ให้ลองแล้วลองดู :) ปรากฎว่ามันใช้งานไม่ได้อย่างสมบูรณ์แบบเหมือน shebang -n +2ดูเหมือนว่าจะถูกตีความว่า-n 2มีเพียงสองบรรทัดสุดท้ายเท่านั้นที่พิมพ์ออกมา นั่นอาจจะคุ้มค่ากับคำถามของตัวเอง
terdon

3
@val ... พฤติกรรมของ shebang นั้นน้อยกว่ามาตรฐานและพกพาได้มากกว่าที่คุณคาดหวัง ดูส่วน "การตีความคำสั่งอาร์กิวเมนต์" ของen.wikipedia.org/wiki/Shebang_(Unix)#Portability
Charles Duffy

@val ที่ทำงานในลินุกซ์ถ้าคุณลบพื้นที่ระหว่างและn +อย่างน้อยใน Linux หลังจากพา ธ ที่สามารถใช้งานได้และช่องว่างอักขระต่อไปนี้จะถือเป็นอาร์กิวเมนต์เดียว ดังนั้นด้วยช่องว่างหางจะเห็นและอาจตัดสินใจที่จะลบ-nอาร์กิวเมนต์ของส่วนนำหน้าที่ไม่ถูกต้องใด ๆ ที่มี (ในกรณีนี้ช่องว่างและ a +) จนกว่าจะได้จำนวน อย่างไรก็ตามเช่นเดียวกับชาร์ลส์ดัฟฟี่กล่าวว่าวิธีปัจจุบันที่พวกเขาทำนี้อาจจะพกพาสะดวกกว่าศูนย์รวมอื่น ๆ
JoL

1
@fluffy พวกเขาสามารถใช้ที่นี่ doc ดูลิงค์จากเอกสาร Fedora ในคำตอบของฉัน พวกเขาใช้ตรงcat <<EOF ...EOFนั้น
Sergiy Kolodyazhnyy

9

ไดเร็กทอรี/etc/grub.d/มีไฟล์เรียกทำงานจำนวนมาก (โดยทั่วไปคือเชลล์สคริปต์ แต่สามารถเรียกใช้งานชนิดไฟล์อื่น ๆ ได้) เมื่อใดก็ตามที่grub-mkconfigได้รับการดำเนินการ (เช่นถ้าคุณเรียกใช้update-grubแต่ยังเมื่อคุณติดตั้งแพคเกจเคอร์เนลที่ปรับปรุงซึ่งมักจะมีตะขอหลังการติดตั้งที่บอกให้ผู้จัดการแพคเกจการปรับปรุงgrub.cfg) พวกเขาทั้งหมดจะดำเนินการตามลำดับตัวอักษร เอาท์พุทของพวกเขาทั้งหมดได้รับการตัดแบ่งและท้ายในไฟล์ที่/boot/grub/grub.cfgมีส่วนหัวที่เรียบร้อยที่แสดงส่วนที่มาจาก/etc/grub.d/ไฟล์ที่

ไฟล์หนึ่งไฟล์40_customนี้ถูกออกแบบมาเพื่อให้คุณสามารถเพิ่มรายการ / บรรทัดได้อย่างง่ายดายgrub.cfgเพียงแค่พิมพ์ / วางลงในไฟล์นี้ สคริปต์อื่น ๆ ในไดเรกทอรีเดียวกันทำงานที่ซับซ้อนมากขึ้นเช่นการค้นหาเมล็ดหรือระบบปฏิบัติการที่ไม่ใช่ลินุกซ์และสร้างรายการเมนูสำหรับพวกเขา

เพื่ออนุญาตให้grub-mkconfigจัดการไฟล์เหล่านั้นในลักษณะเดียวกัน (ดำเนินการและรับเอาต์พุต) 40_customเป็นสคริปต์และใช้exec tail -n +3 $0กลไกนี้เพื่อส่งออกเนื้อหา (ลบส่วนหัว "") หากไม่ใช่ไฟล์ที่ปฏิบัติการได้คุณupdate-grubจะต้องมีข้อยกเว้นพิเศษในการเขียนโค้ดเพื่อใช้เนื้อหาข้อความที่แท้จริงของไฟล์นี้แทนที่จะเรียกใช้งานได้เหมือนกับไฟล์อื่น ๆ ทั้งหมด แต่ถ้าหากคุณ (หรือผู้สร้างการแจกจ่าย linux อื่น) ต้องการให้ชื่อนี้เป็นไฟล์อื่นล่ะ หรือถ้าคุณไม่รู้เกี่ยวกับข้อยกเว้นและสร้างเชลล์สคริปต์ชื่อ40_custom?

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับgrub-mkconfigและ/etc/grub.d/*ในคู่มือการใช้งาน GNU ด้วง (แม้ว่าจะพูดส่วนใหญ่เกี่ยวกับตัวเลือกที่คุณสามารถตั้งค่าใน/etc/default/grub) และนอกจากนี้ยังควรจะเป็นไฟล์ที่ระบุว่าไฟล์เหล่านี้ได้รับการดำเนินการไปยังแบบฟอร์ม/etc/grub.d/READMEgrub.cfg


1
ในขณะที่คำตอบของ terdon อธิบายถึงวิธีการทำงานของสาย แต่อันนี้ให้เหตุผลการออกแบบที่น่าเชื่อถือมากขึ้นสำหรับเหตุผลที่ GRUB ทำสิ่งนี้
JoL

คำตอบที่ดี จนถึงจุดที่คุณมีข้อยกเว้นพิเศษ - ข้อยกเว้น "ง่ายกว่า" อาจมีสองไดเรกทอรีเช่น/etc/grub.d/execไฟล์ / สคริปต์ exectuable และ/etc/grub.d/staticไฟล์ข้อความธรรมดาหรือตัวบ่งชี้อื่น ๆ เพื่อแยกความแตกต่างเหล่านี้ อย่างไรก็ตามนี่คือการตัดสินใจออกแบบที่พวกเขาไปด้วย
Stobor

3

TL; DR : เป็นเคล็ดลับในการเพิ่มรายการใหม่ให้กับไฟล์

จุดทั้งหมดอธิบายไว้ในหนึ่งในหน้า Wiki ของ Ubuntu บนด้วง :

  1. ไฟล์ที่ปฏิบัติการได้เท่านั้นที่สร้างเอาต์พุตไปที่ grub.cfg ระหว่างการดำเนินการ update-grub

เอาต์พุตของสคริปต์ใน/etc/grub.d/กลายเป็นเนื้อหาของgrub.cfgไฟล์

ตอนนี้จะexecทำอะไร? สามารถส่งออกสายอีกครั้งสำหรับสคริปต์ทั้งหมดหรือหากมีคำสั่งให้ - คำสั่งที่กล่าวถึงจะมาแทนที่และแทนที่กระบวนการสคริปต์ สิ่งที่ครั้งหนึ่งเคยเชลล์สคริปต์กับ PID 1234 ตอนนี้คือtailคำสั่งกับ PID 1234

ตอนนี้คุณรู้แล้วว่าจะtail -n +3 $0พิมพ์ทุกอย่างหลังจากบรรทัดที่ 3 ในสคริปต์เอง แล้วทำไมเราต้องทำเช่นนี้? ถ้าด้วงใส่ใจเฉพาะเรื่องผลผลิตเราก็ทำได้เช่นกัน

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ในบรรทัดสุดท้าย เพียงแค่เพิ่มเมนูเข้าไปในไฟล์และทำได้ด้วย นอกจากนี้หากคุณกำลังเขียนสคริปต์การเพิ่มเมนูผู้เข้าร่วมคุณสามารถผนวกผ่าน>>ในเชลล์กับไฟล์นี้

ดูสิ่งนี้ด้วย:


แต่ทำไมด้วงไม่อ่านไฟล์โดยตรง? คุณมีความคิดว่าทำไมพวกเขาถึงเลือกที่จะให้มันใช้งานได้แทน? มันจะง่ายกว่ามากหากให้พวกเขาเป็นไฟล์ข้อความอย่างง่ายซึ่งอ่านโดยกระบวนการใดก็ตามที่สร้างเมนู แต่พวกเขาเลือกที่จะทำให้พวกมันสามารถใช้งานได้และเพิ่มวิธีนี้ (เรียบร้อย) แต่มีวิธีแก้ไขที่ซับซ้อน เป็นเพราะด้วงมีภาษาสคริปต์ของตัวเองในขณะที่ฉันโพสต์ในคำตอบของฉัน?
terdon

@terdon ฉันไม่คิดว่าจะต้องทำอะไรกับภาษาด้วงตัวเอง คุณไม่จำเป็นต้องใช้#!/bin/shในกรณีนี้ ฉันสงสัยว่านี่เป็นเพียงเหตุผลทางประวัติศาสตร์ (ดำเนินการจากฐานรหัสPUPA ) และการตัดสินใจออกแบบที่ได้รับแรงบันดาลใจจากการเขียนสคริปต์ประเภท SysV
Sergiy Kolodyazhnyy

@terdon คำถามนี้เป็นแรงบันดาลใจจริง: unix.stackexchange.com/q/492966/85039
Sergiy Kolodyazhnyy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.