ฉันเคยเห็นหัวข้อที่คล้ายกันสองสามข้อ แต่พวกเขาอ้างถึงไม่ได้อ้างอิงตัวแปรซึ่งฉันรู้ว่าอาจนำไปสู่ผลลัพธ์ที่ไม่พึงประสงค์
ฉันเห็นรหัสนี้และสงสัยว่าถ้าเป็นไปได้ที่จะฉีดบางสิ่งให้ทำงานเมื่อบรรทัดของรหัสนี้ทำงาน:
echo run after_bundle
ฉันเคยเห็นหัวข้อที่คล้ายกันสองสามข้อ แต่พวกเขาอ้างถึงไม่ได้อ้างอิงตัวแปรซึ่งฉันรู้ว่าอาจนำไปสู่ผลลัพธ์ที่ไม่พึงประสงค์
ฉันเห็นรหัสนี้และสงสัยว่าถ้าเป็นไปได้ที่จะฉีดบางสิ่งให้ทำงานเมื่อบรรทัดของรหัสนี้ทำงาน:
echo run after_bundle
คำตอบ:
สำหรับกรณีเฉพาะ
echo run after_bundle
ไม่จำเป็นต้องมีการอ้างอิง ไม่จำเป็นต้องมีการอ้างถึงเนื่องจากอาร์กิวเมนต์echo
เป็นสตริงแบบสแตติกที่ไม่มีการขยายตัวแปรหรือการแทนที่คำสั่ง ฯลฯ พวกมันคือ "แค่สองคำ" (และเมื่อStéphaneชี้ให้เห็นพวกมันถูกสร้างขึ้นจากชุดอักขระแบบพกพา )
"อันตราย" มาเมื่อคุณจัดการกับข้อมูลตัวแปรที่เชลล์อาจขยายหรือตีความ ในกรณีเช่นนี้ต้องระมัดระวังว่าเชลล์ทำสิ่งที่ถูกต้องและผลลัพธ์นั้นเป็นสิ่งที่ตั้งใจไว้
คำถามสองข้อต่อไปนี้มีข้อมูลที่เกี่ยวข้องเกี่ยวกับเรื่องนั้น:
echo
บางครั้งใช้เพื่อ "ป้องกัน" คำสั่งที่อาจเป็นอันตรายในคำตอบในเว็บไซต์นี้ ตัวอย่างเช่นฉันอาจแสดงวิธีลบไฟล์หรือย้ายไฟล์ไปยังปลายทางใหม่โดยใช้
echo rm "${name##*/}.txt"
หรือ
echo mv "$name" "/new_dir/$newname"
สิ่งนี้จะเอาท์พุทคำสั่งบนเครื่องแทนการลบหรือเปลี่ยนชื่อไฟล์ จากนั้นผู้ใช้สามารถตรวจสอบคำสั่งตัดสินใจว่าพวกเขาดูโอเคลบecho
และเรียกใช้อีกครั้ง
คำสั่งของคุณecho run after_bundle
อาจเป็นคำสั่งให้กับผู้ใช้หรืออาจเป็นรหัส "คอมเม้นท์" ที่อันตรายเกินกว่าที่จะรันโดยไม่ทราบผลที่ตามมา
การใช้echo
สิ่งนี้จะต้องรู้ว่าคำสั่งที่แก้ไขแล้วและต้องรับประกันว่าคำสั่งที่แก้ไขแล้วนั้นปลอดภัยจริง ๆ(มันอาจจะไม่ได้ถ้ามันมีการเปลี่ยนเส้นทางและการใช้มันบนไพพ์ไลน์นั้นไม่ทำงาน ฯลฯ )
echo rm "first file.txt" "second file.txt"
มันแตกต่างจากecho rm "first" "file.txt" "second" "file.txt"
อะไร หากคุณต้องการสร้างคำสั่งเชลล์เป็นเอาต์พุตคุณต้องใช้printf '%q ' rm "first file.txt" "second file.txt"; echo
หรือสิ่งที่เทียบเท่าที่สร้างการอ้างอิงไวยากรณ์ใหม่ที่ประเมินถึงการargv
ส่งผ่าน
sh
ไม่ใช่รูปแบบที่ผิดปกติและเห็นผู้คนถามว่า "ทำไมจึงfoo
ทำงานเมื่อฉันเรียกใช้บนบรรทัดคำสั่ง แต่สคริปต์นี้ที่ปล่อยสตริงที่แน่นอนที่echo
อยู่ด้านหน้าของบรรทัดไม่ได้หรือไม่ " เกิดขึ้นตลอดเวลาที่นี่ ยิ่งไปกว่านั้นจุดบกพร่องในการส่งออกจะไม่เป็นประโยชน์หากซ่อนข้อบกพร่องของคุณ - และหากข้อบกพร่องของคุณเกี่ยวข้องกับการอ้างอิงแล้วecho
จะไม่เปิดเผยพวกเขา
เพียงแค่บันทึกพิเศษที่ด้านบนของ@ คำตอบที่ดี
echo run after_bundle
ใช้ได้เพราะไม่มีอักขระใด ๆ ในอาร์กิวเมนต์ 3 ตัวที่ส่งผ่านเพื่อให้echo
มีอักขระที่พิเศษสำหรับเชลล์
และ (จุดพิเศษที่ฉันต้องการทำที่นี่) ไม่มีระบบภาษาที่ไบต์เหล่านั้นสามารถแปลเป็นอักขระที่พิเศษสำหรับเชลล์
ตัวละครทุกคนที่อยู่ในสิ่งที่POSIX เรียกชุดอักขระแบบพกพา อักขระเหล่านั้นควรมีอยู่และเข้ารหัสเหมือนกันในชุดอักขระทั้งหมดในระบบ POSIX ²
ดังนั้นบรรทัดคำสั่งนั้นจะถูกตีความเหมือนกันโดยไม่คำนึงถึงโลแคล
ตอนนี้ถ้าเราเริ่มใช้ตัวละครนอกชุดตัวละครแบบพกพามันเป็นความคิดที่ดีที่จะพูดถึงพวกเขาแม้ว่าพวกเขาจะไม่ได้เป็นพิเศษกับเชลล์เพราะในสถานที่อื่นไบต์ที่ประกอบด้วยพวกเขาอาจถูกตีความว่าเป็นตัวละครที่แตกต่างกัน พิเศษให้กับเปลือก โปรดทราบว่ามันไม่ว่าคุณจะใช้echo
หรือคำสั่งอื่น ๆ ปัญหาไม่ได้อยู่ที่echo
แต่ด้วยวิธีที่เชลล์แยกวิเคราะห์รหัสของมัน
เช่นใน UTF-8:
echo voilà | iconv -f UTF-8 -t //TRANSLIT
ที่à
ถูกเข้ารหัสเป็น 0xc3 0xa0 ตอนนี้ถ้าคุณมีบรรทัดของรหัสนั้นในเชลล์สคริปต์และเชลล์สคริปต์นั้นถูกเรียกใช้โดยผู้ใช้ที่ใช้โลแคลที่มีชุดอักขระไม่ใช่ UTF-8 ไบต์สองตัวนั้นอาจทำให้อักขระต่างกันมาก
ตัวอย่างเช่นในfr_FR.ISO8859-15
สถานที่เกิดเหตุเป็นภาษาฝรั่งเศสทั่วไปโดยใช้ชุดอักขระไบต์เดียวมาตรฐานที่ครอบคลุมภาษาฝรั่งเศส (เช่นเดียวกับที่ใช้สำหรับภาษายุโรปตะวันตกส่วนใหญ่รวมถึงภาษาอังกฤษ) ที่ 0xc3 ไบต์ถูกตีความว่าเป็นÃ
ตัวอักษรและ 0xa0 เป็นไม่ใช่ ตัวละครทำลายพื้นที่
และในบางระบบเช่นNetBSD³นั้นพื้นที่ที่ไม่ทำลายนั้นถือเป็นอักขระว่าง (ซึ่งisblank()
จะคืนค่าจริงมันถูกจับคู่ด้วย[[:blank:]]
) และเชลล์เช่นbash
นั้นจึงถือว่าเป็นตัวคั่นโทเค็นในไวยากรณ์
ซึ่งหมายความว่าแทนที่จะเรียกใช้echo
ด้วย$'voil\xc3\xa0'
อาร์กิวเมนต์พวกเขาเรียกใช้ด้วย$'voil\xc3'
อาร์กิวเมนต์ซึ่งหมายความว่าจะไม่พิมพ์voilà
อย่างถูกต้อง
จะได้รับมากยิ่งกับชุดตัวอักษรภาษาจีนเช่น BIG5, BIG5-HKSCS, GB18030, GBK ซึ่งมีตัวละครหลายคนที่มีการเข้ารหัสที่มีการเข้ารหัสเช่นเดียวกับ|
, `
, \
(เพื่อชื่อที่เลวร้ายที่สุด) (ยังเป็นที่น่าหัวเราะ SJIS อาคาไมโครซอฟท์คันจิยกเว้น ว่ามันเป็น¥
แทน\
แต่ก็ยังถือว่าเป็น\
เครื่องมือมากที่สุดเท่าที่มันเข้ารหัสเป็น 0x5c มี)
ตัวอย่างเช่นหากในzh_CN.gb18030
ภาษาจีนคุณเขียนสคริปต์เช่น:
echo 詜 reboot
สคริปต์นั้นจะแสดงผล詜 reboot
ในโลแคลโดยใช้ GB18030 หรือ GBK 唰 reboot
ในโลแคลที่ใช้ BIG5 หรือ BIG5-HKSCS แต่ในโลแคล C ที่ใช้ ASCII หรือโลแคลที่ใช้ ISO8859-15 หรือ UTF-8 จะทำให้เกิดreboot
การเรียกใช้เนื่องจากการเข้ารหัส GB18030 จาก詜
is 0xd4 0x7c และ 0x7c เป็นการเข้ารหัสของ|
ใน ASCII ดังนั้นเราจึงทำงาน:
echo �| reboot
( นั้นแทน 0xd4 ไบต์ถูกเรนเดอร์ในโลแคล) ตัวอย่างการใช้อันตรายน้อยกว่าuname
แทนreboot
:
$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$
( uname
ถูกเรียกใช้)
ดังนั้นคำแนะนำของฉันคือการอ้างอิงสตริงทั้งหมดที่มีอักขระนอกชุดอักขระแบบพกพา
อย่างไรก็ตามโปรดทราบว่าเนื่องจากการเข้ารหัส\
และ`
พบในการเข้ารหัสของอักขระเหล่านั้นบางอย่างจะดีกว่าที่จะไม่ใช้\
หรือ"..."
หรือ$'...'
(ภายในซึ่ง`
และ / หรือ\
ยังคงเป็นพิเศษ) แต่'...'
แทนที่จะอ้างถึงตัวละครนอกชุดอักขระแบบพกพา
ฉันไม่ทราบว่าระบบใดที่มีโลแคลที่ชุดอักขระมีอักขระใด ๆ (นอกเหนือจาก'
ตัวของมันเอง) ซึ่งการเข้ารหัสประกอบด้วยการเข้ารหัส'
ดังนั้นผู้ที่'...'
ควรจะปลอดภัยที่สุด
โปรดทราบว่าเชลล์จำนวนมากยังรองรับ$'\uXXXX'
สัญลักษณ์เพื่อแสดงอักขระตามจุดโค้ด Unicode ในเชลล์เช่นzsh
และbash
อักขระจะถูกเข้ารหัสในชุดอักขระของโลแคล (แม้ว่าอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิดหากชุดอักขระนั้นไม่มีอักขระนั้น) ที่ช่วยให้คุณหลีกเลี่ยงการใส่อักขระที่ไม่ใช่ ASCII ในรหัสเชลล์ของคุณ
ดังนั้นข้างต้น:
echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'
หรือ:
echo $'voil\u00e0'
echo $'\u8a5c reboot'
(ด้วยข้อแม้มันสามารถทำลายสคริปต์เมื่อทำงานในสถานที่ที่ไม่มีตัวอักษรเหล่านั้น)
หรือดีกว่าเนื่องจาก\
เป็นพิเศษสำหรับecho
(หรืออย่างน้อยการปรับใช้บาง echo
อย่างอย่างน้อยที่สอดคล้องกับ Unix):
printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'
(โปรดทราบว่า\
นี่เป็นพิเศษในอาร์กิวเมนต์แรกprintf
ดังนั้นอักขระที่ไม่ใช่ ASCII จึงหลีกเลี่ยงได้ดีกว่าในกรณีที่อาจมีการเข้ารหัส\
)
โปรดทราบว่าคุณสามารถทำได้เช่นกัน:
'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'
(นั่นจะเกินความจริง แต่จะให้ความอุ่นใจถ้าคุณไม่แน่ใจว่าตัวละครตัวไหนอยู่ในชุดตัวละครแบบพกพา)
ตรวจสอบให้แน่ใจว่าไม่เคยใช้การ`...`
ทดแทนคำสั่งในรูปแบบโบราณ(ซึ่งแนะนำการประมวลผลแบ็กสแลชอีกระดับหนึ่ง) แต่ให้ใช้$(...)
แทน
¹เทคนิคecho
ถูกส่งผ่านไปยังเป็นอาร์กิวเมนต์กับecho
ยูทิลิตี้ (เพื่อบอกว่าวิธีการที่จะถูกเรียก) มันเป็นargv[0]
และargc
เป็น 3 แม้ว่าจะอยู่ในเปลือกหอยส่วนใหญ่ในปัจจุบันecho
จะ builtin เพื่อให้exec()
ของ/bin/echo
ไฟล์ที่มีรายชื่อของ 3 ข้อโต้แย้งจะถูกจำลองโดย เปลือก. นอกจากนี้ยังเป็นเรื่องธรรมดาที่จะพิจารณารายการอาร์กิวเมนต์ที่ขึ้นต้นด้วยตัวที่สอง ( argv[1]
ไปargv[argc - 1]
) ตามที่เป็นคำสั่งที่ดำเนินการโดยส่วนใหญ่
²ข้อยกเว้นที่น่าสังเกตว่าการเปิ่นja_JP.SJIS
สถานระบบ FreeBSD มี charset ไม่มี\
มิได้~
ตัวละคร!
³โปรดทราบว่าในขณะที่ระบบจำนวนมาก (FreeBSD, Solaris ไม่ใช่ GNU) ให้พิจารณา U + 00A0 [[:blank:]]
ในรูปแบบ UTF-8 แต่บางแห่งในสถานที่อื่น ๆ เช่นที่ใช้ ISO8859-15 อาจหลีกเลี่ยงปัญหาประเภทนี้ได้
echo
... " ฉันนับแค่ 2 ข้อโต้แย้งที่ถูกส่งผ่านไปยังคำสั่งecho
อาร์กิวเมนต์ที่ฉันสามารถนับได้run
และafter_bundle
สนใจที่จะอธิบายว่าคุณเป็นอย่างไร นับและได้รับ 3 ข้อโต้แย้ง?
echo
) ดู(exec -a foo /bin/echo --help)
ในระบบ GNU และด้วยเชลล์ GNU สำหรับวิธีส่งอาร์กิวเมนต์แรกไปยัง/bin/echo
ยูทิลิตีโดยพลการ
$0
และพารามิเตอร์ตำแหน่งในเปลือกหอย
iconv
ในการที่จะถูกแปลงเป็นESC
'
ลอง (เป็นตัวอย่าง):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
'
จะถูกแปลงเป็น ลองprintf '\u2804' | iconv -f utf8 -t BRF | xxd
ดู มีการเข้ารหัสที่มีจำนวนมากของ codepoints '
ที่กลายเป็น รอบ 8695 codepoints ใน UCS-4 '
กลายเป็น ลองprintf '\U627' | iconv -cf utf-8 -t UCS-4
ดู หลาย (37) การเข้ารหัสแปลงตัวอักษร 0x127 '
ไป ลองprintf '\U127' | iconv -cf utf8 -t UCS2 |xxd