ในส่วนหัวของสคริปต์ 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
จึงมีประโยชน์มาก อาจมีรายละเอียดเกี่ยวกับสิ่งนี้ แต่นี่คือเหตุผลของฉัน