Bash -c พร้อมพารามิเตอร์ตำแหน่ง


15

โดยปกติ$0ในสคริปต์จะตั้งชื่อของสคริปต์หรือสิ่งที่มันถูกเรียกว่า (รวมถึงเส้นทาง) อย่างไรก็ตามถ้าฉันใช้bashกับ-cตัวเลือกให้$0ตั้งค่าเป็นอาร์กิวเมนต์แรกที่ส่งผ่านหลังจากสตริงคำสั่ง:

bash -c 'echo $0 ' foo bar 
# foo 

ผลดูเหมือนว่าพารามิเตอร์ตำแหน่งได้รับการเลื่อน $0แต่รวมถึง อย่างไรก็ตามshiftในสตริงคำสั่งจะไม่ส่งผลกระทบ$0(ตามปกติ):

bash -c 'echo $0; shift; echo $0 ' foo bar
# foo
# foo

เหตุใดจึงมีพฤติกรรมแปลก ๆ สำหรับสตริงคำสั่ง โปรดทราบว่าฉันกำลังมองหาเหตุผลเหตุผลที่อยู่เบื้องหลังการใช้พฤติกรรมแปลก ๆ


หนึ่งสามารถคาดเดาได้ว่าสตริงคำสั่งดังกล่าวไม่จำเป็นต้องใช้$0พารามิเตอร์ตามปกติที่กำหนดไว้ดังนั้นสำหรับการประหยัดก็ยังใช้สำหรับอาร์กิวเมนต์ปกติ อย่างไรก็ตามในกรณีนั้นพฤติกรรมของshiftคี่ ความเป็นไปได้อีกอย่างหนึ่ง$0คือใช้เพื่อกำหนดพฤติกรรมของโปรแกรม (a la bashเรียกว่าshหรือvimเรียกว่า as vi) แต่ไม่สามารถทำได้เนื่องจาก$0ที่นี่จะเห็นได้เฉพาะในสตริงคำสั่งเท่านั้นและไม่ใช่โดยโปรแกรมที่เรียกว่าภายใน ฉันไม่สามารถคิดถึงการใช้งานอื่น ๆ$0ได้ดังนั้นฉันจึงสูญเสียที่จะอธิบายเรื่องนี้


ตามบันทึกข้างฉันได้เห็นการใช้งาน-สำหรับอาร์กิวเมนต์เป็นสำนวนเหมือนใน$0 sh -c 'foo $1 $2' - a bวิธีนี้ดูปกติดี (เมื่อคุณค้นพบความ-หมายนั่นคือ)
Volker Siegel

คุณยังสามารถecho 'echo the other side of this pipe globs "$@"' | sh -s -- *แต่โชคไม่ดีที่$0โดยทั่วไปไม่พารามิเตอร์ที่กำหนดได้ด้วย-sตัวเลือก tream ... มันสามารถนำมาใช้ในหลายรูปแบบเดียวกันxargsโดยทั่วไปแม้ว่า และอื่น ๆ นอกเหนือจาก
mikeserv

@VolkerSiegel ปกติมากขึ้น--แล้วที่จะมีการตีความปกติของ 'จากที่นี่เริ่มขัดแย้ง' ที่เห็นในโปรแกรมอื่น ๆ จากนั้นอีกครั้งซึ่งอาจสร้างความสับสนให้กับผู้ที่ไม่คุ้นเคยกับ-cการคิดว่า--จริง ๆ แล้วมีการตีความนั้น
muru

คำตอบ:


10

ที่ให้โอกาสคุณในการตั้งค่า / เลือก$0เมื่อใช้สคริปต์แบบอินไลน์ มิฉะนั้นก็จะเป็น$0bash

จากนั้นคุณสามารถทำเช่น:

$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter null or not set

ไม่ใช่กระสุนทุกตัวที่ใช้ในการทำเช่นนั้น เชลล์เป้าหมายได้ทำ Korn (และ Almquist) shell เลือกที่จะให้พารามิเตอร์ตัวแรกไป$1แทน ในที่สุด POSIX ก็ไปตามทางของบอร์นดังนั้นkshและashกลับไปหาอนุพันธ์ในภายหลัง (ดูเพิ่มเติมที่http://www.in-ulm.de/~mascheck/various/find/#shell ) นั่นหมายความว่าเป็นเวลานานสำหรับsh(ซึ่งขึ้นอยู่กับระบบที่ใช้ Bourne, Almquist หรือ Korn shell) คุณไม่ทราบว่าการโต้เถียงครั้งแรกนั้นเกิดขึ้น$0หรือ$1เพื่อความสะดวกในการพกพาคุณต้องทำสิ่งต่าง ๆ เช่น:

sh -c 'echo foo in "$1"' foo foo

หรือ:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

โชคดีที่ POSIX ได้ระบุพฤติกรรมใหม่ที่อาร์กิวเมนต์แรกเข้า$0มาดังนั้นตอนนี้เราสามารถทำได้:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt

ฉันเพิ่งวิ่ง: bash -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txtบน Ubuntu 14.04 ทุบตีversion 4.3.11(1)-releaseและฉันได้รับ: txt files are *.txt. Oo
muru

2
@muru ซึ่งถูกต้อง (และคุณอาจไม่มีtxtไฟล์ในไดเรกทอรีปัจจุบัน) ดูเพิ่มเติมbash -c 'echo "${1?}"' foo
Stéphane Chazelas

อ่าใช่ นั่นเป็นตัวอย่างที่มีประโยชน์ในทางปฏิบัติ
muru

1
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txtเป็นสิ่งประดิษฐ์ที่น่าอัศจรรย์ใจ!
iruvar

หรือคุณใช้วิธีแก้ปัญหาอย่างสมบูรณ์ (แนะนำโดยStéphane Chazelas ใน comp.unix.shell) SHELL -c 'shift $1; command' 2 1 arg1 arg2 ... haha ...
mikeserv

3

พฤติกรรมนี้กำหนดโดย POSIX :

sh -c command_name [อาร์กิวเมนต์ ... ]

อ่านคำสั่งจาก command_string ตัวถูกดำเนินการ ตั้งค่าพารามิเตอร์พิเศษ 0 (ดูพารามิเตอร์พิเศษ ) จากค่าของตัวถูกดำเนินการ command_name และพารามิเตอร์ตำแหน่ง ($ 1, $ 2 และอื่น ๆ ) ตามลำดับจากตัวถูกดำเนินการอาร์กิวเมนต์ที่เหลือ

สำหรับสาเหตุที่คุณต้องการพฤติกรรมนั้นสิ่งนี้จะทำให้ช่องว่างระหว่างสคริปต์และ-cสตริงราบรื่นขึ้น คุณสามารถแปลงระหว่างทั้งสองได้โดยตรงโดยไม่มีการเปลี่ยนแปลงพฤติกรรม พื้นที่อื่น ๆ พึ่งพาสิ่งเหล่านี้เหมือนกัน

นอกจากนี้ยังเป็นไปตามการทำงานของข้อโต้แย้งของโปรแกรมโดยทั่วไป: สิ่งนี้ท้ายที่สุดก็คือการเรียกใช้execฟังก์ชันใดฟังก์ชันหนึ่งซึ่งอาร์กิวเมนต์แรกที่มีให้ก็เป็นเช่น$0กัน บางครั้งคุณต้องการค่าพิเศษที่นั่นและไม่มีวิธีอื่นที่จะได้รับ เนื่องจากมีอาร์กิวเมนต์อยู่มันจะต้องแมปกับบางสิ่งและผู้ใช้จะต้องสามารถตั้งค่าสิ่งนั้นได้

ความสอดคล้องนี้ (และอุบัติเหตุทางประวัติศาสตร์น่าจะเกิดขึ้น) นำไปสู่สถานการณ์ที่คุณพบ


คุณช่วยยกตัวอย่างอันที่สอง (หวังว่าจะเป็นโลกแห่งความจริง) ได้หรือไม่?
muru

argv[0]ระมัดระวังกับการเปรียบเทียบกับ $0ตั้งค่าเป็นค่าที่argv[0]ส่งผ่านไปexecve()เมื่อต้องการเท่านั้นshหรือ bashสำหรับสคริปต์$0ถูกตั้งค่าเป็นพา ธ ที่กำหนดเป็นอาร์กิวเมนต์ 1, 2 ... ไปยังล่าม (และสำหรับสคริปต์ที่ดำเนินการโดยตรงซึ่งมาจากอาร์กิวเมนต์พา ธ แรกเพื่อ execve ())
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.