วิธีใช้หลายอาร์กิวเมนต์สำหรับ awk กับ shebang (เช่น #!)


118

ฉันต้องการเรียกใช้สคริปต์gawk--re-intervalโดยใช้ shebang แนวทาง "ไร้เดียงสา" ของ

#!/usr/bin/gawk --re-interval -f
... awk script goes here

ใช้งานไม่ได้เนื่องจาก gawk ถูกเรียกด้วยอาร์กิวเมนต์แรก"--re-interval -f"(ไม่ถูกแยกออกจากช่องว่าง) ซึ่งไม่เข้าใจ มีวิธีแก้ปัญหาสำหรับสิ่งนั้นหรือไม่?

แน่นอนคุณไม่สามารถเรียก gawk ได้โดยตรง แต่รวมไว้ในเชลล์สคริปต์ที่แยกอาร์กิวเมนต์แรกหรือสร้างเชลล์สคริปต์ที่เรียกว่า gawk และใส่สคริปต์ลงในไฟล์อื่น แต่ฉันสงสัยว่ามีวิธีทำหรือไม่ นี้ภายในไฟล์เดียว

ลักษณะการทำงานของเส้น Shebang แตกต่างกันไปในแต่ละระบบ - อย่างน้อยในCygwinจะไม่แบ่งอาร์กิวเมนต์ด้วยช่องว่าง ฉันแค่สนใจว่าจะทำอย่างไรกับระบบที่มีพฤติกรรมเช่นนั้น สคริปต์ไม่ได้มีไว้สำหรับพกพา


1
การทดลองโง่ ๆ ที่ฉันเพิ่งทำคือการใช้สคริปต์หนึ่งโดยใช้สคริปต์อื่นในบรรทัด shebang ซึ่งแยกอาร์กิวเมนต์ได้อย่างถูกต้อง
Hasturkun

@Hasturkun ซึ่งทำให้เกิดปัญหาอื่นขึ้นมาว่าลักษณะการทำงานของเส้น shebang ยังแตกต่างกันไปในแต่ละระบบและเขียนว่าโปรแกรมที่เรียกใช้สามารถเป็นสคริปต์ได้หรือไม่
dubiousjim


ด้วยเวอร์ชันล่าสุดของ gawk (> = 4.0) --re-intervalไม่จำเป็นอีกต่อไป (โปรดดู [ gnu.org/software/gawk/manual/… )

คำตอบ:


25

สิ่งนี้ดูเหมือนจะใช้ได้กับฉันด้วย (g) awk

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"


# The real awk program starts here
{ print $0 }

สังเกตการ#!รัน/bin/shดังนั้นสคริปต์นี้จึงถูกตีความเป็นเชลล์สคริปต์ก่อน

ตอนแรกฉันแค่ลอง"exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"แต่ awk ถือว่าเป็นคำสั่งและพิมพ์อินพุตทุกบรรทัดโดยไม่มีเงื่อนไข นั่นคือเหตุผลที่ฉันใส่arbitrary_long_name==0- มันควรจะล้มเหลวตลอดเวลา คุณสามารถแทนที่ด้วยสตริงที่พูดพล่อยๆ โดยทั่วไปฉันกำลังมองหาเงื่อนไขเท็จใน awk ที่จะไม่ส่งผลเสียต่อเชลล์สคริปต์

ในเชลล์สคริปต์ที่arbitrary_long_name==0กำหนดตัวแปรที่เรียกว่าชุดมันเท่ากับarbitrary_long_name=0


นี่คือคำตอบของฉัน แต่ฉันสงสัยว่ามันพกพาได้เพียงพอและแข็งแกร่งหรือไม่ ขึ้นอยู่กับเฉพาะbashหรือจะทำงานกับ POSIX ใด ๆsh? และฉันไม่ได้ใช้awkบ่อยนักดังนั้นฉันไม่แน่ใจว่าเคล็ดลับในบรรทัดที่สองเป็นวิธีที่ดีในการบังคับawkให้เพิกเฉยต่อบรรทัด
Aaron McDaid

สิ่งที่ฉันสงสัยคือ +1 แต่อาจจะมองไม่เห็น (ด้วยเหตุนี้การโหวตที่สัมพันธ์กัน)
Aaron Hall

คุณช่วยอธิบายได้ไหมว่าปัญหานี้อาจมีอะไรบ้าง @AaronHall ตราบใดที่ตัวแปรarbitrary_long_nameไม่ปะทะกับตัวแปรที่ใช้ในโปรแกรม awk จริงฉันไม่เห็นปัญหาใด ๆ มีบางอย่างที่ฉันขาดหายไป?
Aaron McDaid

ใช้#!/bin/sh -แทน#!/bin/shเพื่อป้องกันสคริปต์จากการทำงานที่ไม่ถูกต้องในลักษณะที่เป็นอันตรายหากเรียกใช้ด้วยอาร์กิวเมนต์ zeroth ที่มี-เป็นอักขระตัวแรก สิ่งนี้สามารถเกิดขึ้นได้โดยบังเอิญในภาษาโปรแกรมเช่น C ซึ่งง่ายต่อการทำผิดพลาดโดยไม่ได้ตั้งใจโดยลืมส่งชื่อโปรแกรมที่เรียกใช้เป็นส่วนหนึ่งของอาร์เรย์อาร์กิวเมนต์ไปยังexecveฟังก์ชันที่คล้ายกันและหากผู้คนลืมที่จะป้องกันเป็นนิสัยก็สามารถทำได้เช่นกัน ท้ายที่สุดถือเป็นขั้นตอนสุดท้ายในช่องโหว่ที่ใช้ประโยชน์จากเจตนาร้ายซึ่งทำให้ผู้โจมตีได้รับเชลล์แบบโต้ตอบ
mtraceur

161

ไม่เคยมีการระบุบรรทัด Shebang เป็นส่วนหนึ่งของ POSIX, SUS, LSB หรือข้อกำหนดอื่น ๆ AFAIK ยังไม่ได้รับการจัดทำเป็นเอกสารอย่างถูกต้อง

มีมติคร่าวๆเกี่ยวกับสิ่งที่มันไม่เป็น: ใช้ทุกอย่างระหว่าง!และ\nและexecมัน สมมติฐานคือทุกอย่างระหว่าง!และ\nเป็นเส้นทางสัมบูรณ์เต็มรูปแบบไปยังล่าม ไม่มีความเห็นเป็นเอกฉันท์ว่าจะเกิดอะไรขึ้นหากมีช่องว่าง

  1. ระบบปฏิบัติการบางระบบถือว่าทุกอย่างเป็นเส้นทาง ท้ายที่สุดแล้วในระบบปฏิบัติการส่วนใหญ่ช่องว่างหรือขีดกลางจะถูกต้องตามกฎหมาย
  2. ระบบปฏิบัติการบางระบบแยกที่ช่องว่างและถือว่าส่วนแรกเป็นเส้นทางไปยังล่ามและส่วนที่เหลือเป็นอาร์กิวเมนต์แต่ละตัว
  3. ระบบปฏิบัติการบางระบบแยกที่ช่องว่างแรกและถือว่าส่วนหน้าเป็นพา ธ ไปยัง interpeter และส่วนที่เหลือเป็นอาร์กิวเมนต์เดียว (ซึ่งก็คือสิ่งที่คุณเห็น)
  4. บางคนไม่สนับสนุนสาย shebang เลยด้วยซ้ำ

โชคดีที่ 1. และ 4. ดูเหมือนจะเสียชีวิตไปแล้ว แต่ 3. ค่อนข้างแพร่หลายดังนั้นคุณจึงไม่สามารถพึ่งพาความสามารถในการส่งผ่านข้อโต้แย้งมากกว่าหนึ่งข้อ

และเนื่องจากสถานที่ตั้งของคำสั่งยังไม่ได้ระบุใน POSIX หรือ SUS คุณมักใช้ว่าอาร์กิวเมนต์เดียวโดยผ่านการปฏิบัติการของชื่อไปenvเพื่อให้มันสามารถตรวจสอบสถานที่ปฏิบัติการของ; เช่น:

#!/usr/bin/env gawk

[เห็นได้ชัดว่านี่ยังถือว่าเป็นเส้นทางเฉพาะสำหรับenvแต่มีเพียงไม่กี่ระบบที่มันอาศัยอยู่/binดังนั้นโดยทั่วไปจึงปลอดภัย สถานที่ตั้งenvมีมาตรฐานมากกว่าสถานที่ตั้งgawkหรือแย่กว่านั้นมากเช่นpythonหรือrubyหรือspidermonkey]

ซึ่งหมายความว่าคุณไม่สามารถใช้อาร์กิวเมนต์ใด ๆได้เลย


1
env ของ FreeBSD มี-Sสวิตช์ที่ช่วยได้ที่นี่ แต่ไม่มีอยู่ใน Linux ของฉันenvและฉันสงสัยว่าไม่มีใน gygwin เช่นกัน @hstoerr ผู้ใช้รายอื่นที่มีสถานการณ์ต่างกันอาจกำลังอ่านคำถามของคุณในภายหลังดังนั้นคำตอบแบบพกพาทั่วไปจึงดีกว่าแม้ว่าตอนนี้คุณจะไม่ต้องการการพกพาก็ตาม
dubiousjim

4
ดังนั้นเราจึงไม่สามารถใช้อาร์กิวเมนต์ใน shebang ได้ แต่ถ้าเราต้องการข้อโต้แย้งโดยวิธีใดก็ตามที่จำเป็นล่ะ? ฉันคาดเดาว่าการแก้ปัญหาคือการเขียนสคริปต์กระดาษห่อเปลือกที่มีและ#!/bin/sh /usr/bin/env gawk --re-interval -f my-script.awkถูกต้องหรือไม่
Rory O'Kane

1
ผมไม่เห็นด้วย. คุณสามารถใช้อาร์กิวเมนต์เดียวได้ ระบบใด ๆ ที่คุณไม่สามารถใช้อาร์กิวเมนต์ใด ๆ ที่ล้มเหลวในการใช้ Unixism แบบดั้งเดิมนี้ซึ่งเป็นสิ่งที่แฮช - ปังคือ หากการไม่ใช้งานเป็นเกมที่ยุติธรรมเราสามารถพูดได้อย่างปลอดภัยว่า#!ตัวเกมนั้นไม่สามารถพกพาได้ ตัวอย่างเช่น Windows ไม่รู้จักหลักการนี้ "โดยกำเนิด" เลย A-โต้แย้งใครได้ปังเป็นสิ่งจำเป็นบน Unix #!/usr/bin/awk -fประเพณีที่สามารถที่จะทำ
Kaz

7
@Kaz: ใช่ แต่เนื่องจากเส้นทางของไบนารีจำนวนมากไม่ได้เป็นมาตรฐานคุณจึงใช้อาร์กิวเมนต์เดียวสำหรับ#!/usr/bin/env rubyหรือชอบ
Jörg W Mittag

3
@Pacerier: เปลี่ยนข้อกำหนด POSIX และรอ 20-30 ปีจนกว่าระบบทั้งหมดจะได้รับการปรับปรุงให้เป็นไปตามสเป็ค
Jörg W Mittag

18

แม้ว่าจะไม่พกพาได้อย่างแน่นอน แต่เริ่มต้นด้วย coreutils 8.30 และตามเอกสารประกอบคุณจะสามารถใช้:

#!/usr/bin/env -S command arg1 arg2 ...

ให้:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

คุณจะได้รับ:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

และในกรณีที่คุณอยากรู้showargsคือ:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

คำตอบเดิมที่นี่


1
FYI, FreeBSD มี -S มาหลายปีแล้ว (ตั้งแต่ 6.0) นี่เป็นการเพิ่มความสามารถในการพกพาสำหรับ coreutils
ฮวน

12

ฉันเจอปัญหาเดียวกันโดยไม่มีวิธีแก้ปัญหาที่ชัดเจนเนื่องจากวิธีจัดการกับช่องว่างใน shebang (อย่างน้อยก็บน Linux)

อย่างไรก็ตามคุณสามารถส่งตัวเลือกต่างๆใน shebang ได้ตราบใดที่เป็นตัวเลือกสั้น ๆและสามารถต่อกันได้ (วิธี GNU)

ตัวอย่างเช่นคุณไม่สามารถมี

#!/usr/bin/foo -i -f

แต่คุณสามารถมี

#!/usr/bin/foo -if

เห็นได้ชัดว่าจะใช้ได้เฉพาะเมื่อตัวเลือกมีค่าเทียบเท่าสั้น ๆ และไม่มีข้อโต้แย้ง


11

ภายใต้ Cygwin และ Linux ทุกอย่างหลังจากเส้นทางของ shebang จะถูกแยกวิเคราะห์ไปยังโปรแกรมเป็นอาร์กิวเมนต์เดียว

เป็นไปได้ที่จะแฮ็คสิ่งนี้โดยใช้awkสคริปต์อื่นภายใน shebang:

#!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}

สิ่งนี้จะดำเนินการ{system("/usr/bin/gawk --re-interval -f " FILENAME); exit}ใน awk
และสิ่งนี้จะดำเนินการ/usr/bin/gawk --re-interval -f path/to/your/script.awkในเชลล์ระบบของคุณ


2
สิ่งนี้จะไม่ได้ผลถ้าคุณผ่านข้อโต้แย้งไปยังสคริปต์
Steven Penny

4
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

เคล็ดลับเปลือกข้างต้น shebang /usr/bin/envเป็นแบบพกพามากกว่า


'' ':' คือการระงับเนื่องจากโซลูชันดั้งเดิมของฉันใช้สำหรับสคริปต์ python ดังนั้น '' ':' จึงบอกให้ล่าม python เพิกเฉยต่อส่วน exec
user3123730

4
ฉันคิดว่าคุณกำลังถูก downvoted เพราะวิธีการแก้ปัญหาของคุณเป็นแต่คำถามนี้เป็นเรื่องเกี่ยวกับpython awk
Aaron McDaid

1
แฮ็คที่ยอดเยี่ยมสำหรับ python
Zaar Hai

3

ในคู่มือ gawk (http://www.gnu.org/manual/gawk/gawk.html) ตอนท้ายของหัวข้อ 1.14 โปรดทราบว่าคุณควรใช้อาร์กิวเมนต์เดียวเท่านั้นเมื่อเรียกใช้ gawk จากบรรทัด shebang มันบอกว่าระบบปฏิบัติการจะปฏิบัติต่อทุกสิ่งหลังจากเส้นทางสู่การจ้องมองเป็นอาร์กิวเมนต์เดียว อาจมีวิธีอื่นในการระบุ--re-intervalตัวเลือก? บางทีสคริปต์ของคุณสามารถอ้างอิงเชลล์ของคุณในบรรทัด shebang รันgawkเป็นคำสั่งและรวมข้อความของสคริปต์ของคุณเป็น "เอกสารที่นี่"


ดูเหมือนว่าไม่มีวิธีอื่นในการระบุตัวเลือก คุณพูดถูก: gawk -f - << EOF, สคริปต์บางบรรทัด, EOF ทำงานได้ แต่มันกีดกันฉันจากการอ่านอินพุตมาตรฐานด้วย gawk
Hans-Peter Störr

เอกสารที่นี่กินสตรีมอินพุตมาตรฐานสำหรับgawkแต่คุณอาจยังสามารถไพพ์บางอย่างในส่วนบน stderr ได้ (นั่นคือเปลี่ยนเส้นทาง stdout ไปยัง stderr ก่อนที่จะไพพ์เข้าสู่สคริปต์นี้) ฉันไม่เคยลองแบบนั้นจริง ๆ แต่ตราบใดที่กระบวนการแรกไม่ได้ส่งสัญญาณอะไรออกมาบน stderr ก็อาจใช้ได้ คุณยังสามารถสร้างไปป์ที่มีชื่อ ( linuxjournal.com/content/using-named-pipes-fifos-bash ) หากต้องการให้แน่ใจว่าไม่มีสิ่งอื่นใดที่ใช้งานได้
bta

3

ทำไมไม่ใช้bashและgawkตัวมันเองเพื่อข้าม Shebang ที่ผ่านมาอ่านสคริปต์และส่งต่อเป็นไฟล์ไปยังอินสแตนซ์ที่สองของgawk [--with-whatever-number-of-params-you-need]?

#!/bin/bash
gawk --re-interval -f <(gawk 'NR>3' $0 )
exit
{
  print "Program body goes here"
  print $1
}

(เช่นเดียว -The สามารถตามธรรมชาติยังทำได้ด้วยเช่นsedหรือtailแต่ผมคิดว่ามีชนิดของความงามบางอย่างขึ้นอยู่เฉพาะในbashและgawkตัวเอง;)


0

เพียงเพื่อความสนุกสนาน: มีวิธีแก้ปัญหาแปลก ๆ ดังต่อไปนี้ที่เปลี่ยนเส้นทาง stdin และโปรแกรมผ่านตัวอธิบายไฟล์ 3 และ 4 คุณยังสามารถสร้างไฟล์ชั่วคราวสำหรับสคริปต์ได้

#!/bin/bash
exec 3>&0
exec <<-EOF 4>&0
BEGIN {print "HALLO"}
{print \$1}
EOF
gawk --re-interval -f <(cat 0>&4) 0>&3

สิ่งหนึ่งที่น่ารำคาญเกี่ยวกับเรื่องนี้: เชลล์ทำการขยายตัวแปรบนสคริปต์ดังนั้นคุณต้องพูดทุก $ (ตามที่ทำในบรรทัดที่สองของสคริปต์) และอาจมากกว่านั้น


-1

สำหรับโซลูชันแบบพกพาให้ใช้awkแทนที่จะgawkเรียกใช้เชลล์ BOURNE มาตรฐาน ( /bin/sh) ด้วย shebang ของคุณและเรียกใช้awkโดยตรงโดยส่งผ่านโปรแกรมบนบรรทัดคำสั่งเป็นเอกสารที่นี่แทนที่จะใช้ stdin:

#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF

หมายเหตุ:ไม่มี-fข้อโต้แย้งกับawk. ที่เหลือstdinให้awkอ่านข้อมูลจาก สมมติว่าคุณได้gawkติดตั้งและบนของคุณPATHซึ่งบรรลุทุกสิ่งที่ฉันคิดว่าคุณกำลังพยายามทำกับตัวอย่างเดิมของคุณ (สมมติว่าคุณต้องการให้เนื้อหาไฟล์เป็นสคริปต์ awk ไม่ใช่อินพุตซึ่งฉันคิดว่าวิธีการ shebang ของคุณจะถือว่าเป็น )


3
นั่นไม่ได้ผลสำหรับฉัน คนทุบตีพูดว่า <<< blabla ทำให้ blabla อยู่ใน stdin คุณหมายถึง << - EOF? ไม่ว่าจะด้วยวิธีใดนั่นก็ทำให้โปรแกรมอยู่ใน stdin เช่นกัน
Hans-Peter Störr
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.