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