ในส่วนหัวของสคริปต์ Bash ความแตกต่างระหว่างสองข้อความเหล่านี้คืออะไร:
#!/usr/bin/env bash#!/usr/bin/bash
เมื่อฉันปรึกษาenv หน้า manฉันได้คำจำกัดความนี้:
env - run a program in a modified environment
มันหมายความว่าอะไร?
ในส่วนหัวของสคริปต์ Bash ความแตกต่างระหว่างสองข้อความเหล่านี้คืออะไร:
#!/usr/bin/env bash
#!/usr/bin/bash
เมื่อฉันปรึกษาenv หน้า manฉันได้คำจำกัดความนี้:
env - run a program in a modified environment
มันหมายความว่าอะไร?
คำตอบ:
การเรียกใช้คำสั่งผ่าน/usr/bin/envมีประโยชน์ในการค้นหาสิ่งที่รุ่นเริ่มต้นของโปรแกรมอยู่ในการรีดenvปัจจุบันของคุณ
ด้วยวิธีนี้คุณไม่จำเป็นต้องค้นหาในสถานที่เฉพาะบนระบบเนื่องจากเส้นทางเหล่านั้นอาจอยู่ในสถานที่ต่างกันในระบบที่แตกต่างกัน ตราบใดที่มันอยู่ในเส้นทางของคุณก็จะพบมัน
ข้อเสียอย่างหนึ่งคือคุณจะไม่สามารถผ่านการโต้แย้งมากกว่าหนึ่งข้อ (เช่นคุณจะไม่สามารถเขียนได้/usr/bin/env awk -f) หากคุณต้องการสนับสนุน Linux เนื่องจากPOSIX นั้นคลุมเครือเกี่ยวกับการตีความบรรทัดและ Linux ตีความทุกอย่างหลังจากแรก ช่องว่างเพื่อแสดงถึงอาร์กิวเมนต์เดียว คุณสามารถใช้/usr/bin/env -Sในบางรุ่นenvเพื่อแก้ไขปัญหานี้ แต่สคริปต์จะกลายเป็นแบบพกพาที่น้อยลงและทำลายระบบที่ค่อนข้างเป็นปัจจุบัน (เช่นแม้กระทั่ง Ubuntu 16.04 หากไม่ใช่ในภายหลัง)
ข้อเสียอีกข้อหนึ่งคือเนื่องจากคุณไม่ได้เรียกใช้ไฟล์ที่เรียกทำงานได้อย่างชัดเจนมันอาจมีข้อผิดพลาดและมีปัญหาด้านความปลอดภัยของระบบผู้ใช้หลายคน (ถ้ามีคนจัดการเพื่อให้สามารถเรียกใช้ไฟล์ปฏิบัติการbashได้
#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash #gives you explicit control on a given system of what executable is called
ในบางสถานการณ์อาจต้องการใช้ครั้งแรก (เช่นการเรียกใช้สคริปต์ python ที่มีหลายรุ่นของ python โดยไม่ต้องทำใหม่บรรทัดปฏิบัติการ) แต่ในสถานการณ์ที่เน้นความปลอดภัยควรเลือกใช้ความปลอดภัยหลังเนื่องจากเป็นการ จำกัด โอกาสในการฉีดโค้ด
#!/usr/bin/env echo Hello /usr/bin/env: echo Hello: No such file or directoryเห็นได้ชัดว่ามันถือว่าเป็นอาร์กิวเมนต์เดียวเพื่อecho Hello /usr/bin/env
envคำสั่งอนุญาตให้ส่งผ่านอาร์กิวเมนต์ไปยังคำสั่งได้อย่างแน่นอน ปัญหาคือความหมายของ#!บรรทัดและขึ้นอยู่กับเคอร์เนล เคอร์เนล Linux ล่าสุดอนุญาตให้ใช้สิ่งต่าง ๆ เช่น#!/usr/bin/env command argsกัน แต่เมล็ด Linux รุ่นเก่าและระบบอื่น ๆ ไม่สามารถทำได้
การใช้#!/usr/bin/env NAMEทำให้เชลล์ค้นหาคู่แรกของ NAME ในตัวแปรสภาพแวดล้อม $ PATH มันจะมีประโยชน์หากคุณไม่ทราบเส้นทางที่แน่นอนหรือไม่ต้องการค้นหา
แทนที่จะกำหนดเส้นทางไปยังล่ามอย่างชัดเจน/usr/bin/bash/โดยใช้คำสั่ง env ล่ามจะค้นหาและเรียกใช้จากที่ใดก็ตามที่พบครั้งแรก สิ่งนี้มีทั้งคว่ำและลง
ถ้าเชลล์สคริปต์เริ่มต้นด้วยการ#!/bin/bashที่พวกเขามักจะทำงานด้วยจากbash /binหากพวกเขาเริ่มต้นด้วย#!/usr/bin/env bashพวกเขาจะค้นหาbashใน$PATHแล้วเริ่มต้นด้วยคนแรกที่พวกเขาสามารถหา
ทำไมสิ่งนี้ถึงมีประโยชน์ สมมติว่าคุณต้องการรันbashสคริปต์ที่ต้องการ bash 4.x หรือใหม่กว่า แต่ระบบของคุณมีเพียงbashติดตั้ง 3.xและในปัจจุบันการแจกจ่ายของคุณไม่ได้ให้รุ่นที่ใหม่กว่าหรือคุณไม่ใช่ผู้ดูแลระบบและไม่สามารถเปลี่ยนสิ่งที่ติดตั้งบนระบบนั้น .
แน่นอนคุณสามารถดาวน์โหลดซอร์สโค้ด bash และสร้าง bash ของคุณเองได้จากศูนย์โดยวาง~/binตัวอย่างไว้ และคุณยังสามารถแก้ไข$PATHตัวแปรของคุณใน.bash_profileไฟล์เพื่อรวม~/binเป็นรายการแรก ( PATH=$HOME/bin:$PATHเช่นที่~จะไม่ขยาย$PATH) ถ้าคุณเรียกตอนนี้bashเชลล์จะค้นหามัน$PATHตามลำดับก่อนดังนั้นจึงเริ่มต้นด้วย~/binมันจะหาตำแหน่งของคุณbashที่ไหน สิ่งเดียวกันที่เกิดขึ้นถ้าค้นหาสคริปต์สำหรับbashการใช้#!/usr/bin/env bashเพื่อให้สคริปต์เหล่านี้จะได้รับการทำงานในระบบของคุณโดยใช้กำหนดเองของคุณbashสร้าง
ข้อเสียอย่างหนึ่งคือสิ่งนี้สามารถนำไปสู่พฤติกรรมที่ไม่คาดคิดเช่นสคริปต์เดียวกันบนเครื่องเดียวกันอาจทำงานกับล่ามที่แตกต่างกันสำหรับสภาพแวดล้อมที่แตกต่างกันหรือผู้ใช้ที่มีเส้นทางการค้นหาที่แตกต่างกันทำให้ปวดหัวทุกชนิด
ข้อเสียที่ใหญ่ที่สุดกับenvคือว่าบางระบบจะช่วยให้หนึ่งอาร์กิวเมนต์ดังนั้นคุณจึงไม่สามารถทำเช่นนี้#!/usr/bin/env <interpreter> <arg>เป็นระบบที่จะเห็น<interpreter> <arg>ว่าเป็นหนึ่งในข้อโต้แย้ง (พวกเขาจะปฏิบัติต่อมันราวกับว่าการแสดงออกถูกยกมา) จึงจะค้นหาล่ามที่มีชื่อว่าenv <interpreter> <arg>โปรดทราบว่านี้ไม่ได้เป็นปัญหาของการที่envคำสั่งตัวเองซึ่งได้รับอนุญาตเสมอพารามิเตอร์หลายที่จะผ่าน แต่มีการแยกวิเคราะห์ shebang envของระบบที่แยกบรรทัดนี้ก่อนที่จะโทรแม้กระทั่ง ในขณะนี้สิ่งนี้ได้รับการแก้ไขในระบบส่วนใหญ่ แต่ถ้าสคริปต์ของคุณต้องการพกพาสะดวกคุณไม่สามารถวางใจได้ว่าสิ่งนี้ได้รับการแก้ไขแล้วในระบบที่คุณจะทำงาน
มันสามารถมีผลกระทบด้านความปลอดภัยเช่นหากsudoไม่ได้กำหนดค่าสภาพแวดล้อมที่สะอาดหรือ$PATHถูกแยกออกจากการล้างข้อมูล ให้ฉันสาธิตสิ่งนี้:
โดยปกติแล้ว/binจะเป็นสถานที่ที่ได้รับการป้องกันเป็นอย่างดีrootสามารถเปลี่ยนแปลงอะไรก็ได้ โฮมไดเร็กตอรี่ของคุณไม่ใช่, โปรแกรมใด ๆ ที่คุณรันสามารถทำการเปลี่ยนแปลงได้. นั่นหมายความว่ารหัสที่เป็นอันตรายอาจวางของปลอมbashลงในไดเรกทอรีที่ซ่อนอยู่บางส่วนปรับเปลี่ยนของคุณ.bash_profileเพื่อรวมไดเรกทอรีนั้นไว้ในของคุณ$PATHดังนั้นสคริปต์ทั้งหมดที่ใช้#!/usr/bin/env bashจะจบลงด้วยการทำงานของปลอมbashนั้น ถ้าsudoเก็บ$PATHไว้คุณกำลังมีปัญหาใหญ่
เช่นพิจารณาเครื่องมือสร้างไฟล์ที่~/.evil/bashมีเนื้อหาดังต่อไปนี้:
#!/bin/bash
if [ $EUID -eq 0 ]; then
echo "All your base are belong to us..."
# We are root - do whatever you want to do
fi
/bin/bash "$@"
มาสร้างสคริปต์ง่ายๆsample.shกันเถอะ:
#!/usr/bin/env bash
echo "Hello World"
หลักฐานของแนวคิด (ในระบบที่sudoเก็บ$PATH):
$ ./sample.sh
Hello World
$ sudo ./sample.sh
Hello World
$ export PATH="$HOME/.evil:$PATH"
$ ./sample.sh
Hello World
$ sudo ./sample.sh
All your base are belong to us...
Hello World
โดยปกติแล้วกระสุนคลาสสิคควรจะอยู่ในนั้น/binและถ้าคุณไม่ต้องการวางไว้ที่นั่นไม่ว่าด้วยเหตุผลใดก็ตามมันไม่ใช่ปัญหาที่จะต้องวาง symlink ใน/binจุดนั้นไปยังตำแหน่งจริง (หรืออาจ/binเป็น symlink) ผมมักจะไปกับและ#!/bin/sh #!/bin/bashมีมากเกินไปที่จะทำลายหากสิ่งเหล่านี้จะไม่ทำงานอีกต่อไป ไม่ใช่ว่า POSIX จะต้องใช้ตำแหน่งเหล่านี้ (POSIX ไม่ได้ทำให้ชื่อพา ธ เป็นมาตรฐานและดังนั้นจึงไม่ได้ทำให้ฟีเจอร์ Shebang เป็นมาตรฐานเลย) แต่มันก็เป็นเรื่องธรรมดามากแม้ว่าระบบจะไม่เสนอให้/bin/shก็ตาม#!/bin/shและรู้ว่าต้องทำอะไรกับมันและมันอาจจะเป็นความเข้ากันได้กับรหัสที่มีอยู่เท่านั้น
แต่สำหรับล่ามที่เป็นทางเลือกที่ทันสมัยและไม่เป็นมาตรฐานเช่น Perl, PHP, Python หรือ Ruby มันไม่ได้ระบุที่ใดก็ตามที่พวกเขาควรจะอยู่ พวกเขาอาจจะมา/usr/binแต่พวกเขาได้เป็นอย่างดีอาจจะอยู่ใน/usr/local/binหรือในสาขาลำดับชั้นที่แตกต่างกันอย่างสิ้นเชิง ( /opt/..., /Applications/...ฯลฯ ) นั่นเป็นเหตุผลที่สิ่งเหล่านี้มักจะใช้#!/usr/bin/env xxxรูปแบบ shebang
ฉันพบว่ามีประโยชน์เพราะเมื่อฉันไม่รู้ env ก่อนที่ฉันจะเริ่มเขียนสคริปต์ฉันทำสิ่งนี้:
type nodejs > scriptname.js #or any other environment
แล้วฉันก็แก้ไขบรรทัดนั้นในไฟล์เป็น shebang
ฉันทำสิ่งนี้เพราะฉันจำไม่ได้ว่าอยู่ที่ไหน nodejs บนคอมพิวเตอร์ของฉัน - / usr / bin / หรือ / bin / ดังนั้นสำหรับฉันenvจึงมีประโยชน์มาก อาจมีรายละเอียดเกี่ยวกับสิ่งนี้ แต่นี่คือเหตุผลของฉัน