โหนด“ / usr / bin / env” ทำหน้าที่อะไรที่จุดเริ่มต้นของไฟล์โหนด


113

ฉันเคยเห็นบรรทัดนี้#!/usr/bin/env nodeในตอนต้นของตัวอย่างบางส่วนnodejsและฉัน googled โดยไม่พบหัวข้อใดที่สามารถตอบเหตุผลของบรรทัดนั้นได้

ลักษณะของคำทำให้การค้นหาไม่ใช่เรื่องง่าย

ฉันอ่านบางส่วนjavascriptและnodejsหนังสือเมื่อเร็ว ๆ นี้และฉันจำไม่ได้เห็นมันในใด ๆ ของพวกเขา

หากคุณต้องการตัวอย่างคุณสามารถดูบทช่วยสอนRabbitMQอย่างเป็นทางการซึ่งมีอยู่ในตัวอย่างเกือบทั้งหมดนี่คือหนึ่งในนั้น:

#!/usr/bin/env node

var amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', function(err, conn) {
  conn.createChannel(function(err, ch) {
    var ex = 'logs';
    var msg = process.argv.slice(2).join(' ') || 'Hello World!';

    ch.assertExchange(ex, 'fanout', {durable: false});
    ch.publish(ex, '', new Buffer(msg));
    console.log(" [x] Sent %s", msg);
  });

  setTimeout(function() { conn.close(); process.exit(0) }, 500);
});

ใครช่วยอธิบายความหมายของบรรทัดนี้ได้ไหม

อะไรคือความแตกต่างถ้าฉันใส่หรือลบบรรทัดนี้? ฉันจำเป็นต้องใช้ในกรณีใดบ้าง?


3
โดยพื้นฐานแล้วจะใช้สภาพแวดล้อมของเชลล์การโทรและบรรจุสภาพแวดล้อมนั้นลงในแอปที่ระบุไว้ ในกรณีนี้node
Marc B

ไม่จริงฉันไม่ได้มาจาก Windows แต่ขอขอบคุณสำหรับการอัปเดตคำตอบของคุณ ฉันแค่รอดูว่าจะมีใครแสดงความคิดเห็นที่แตกต่างกันอีกหรือไม่ มีเพียงสิ่งเดียวที่ฉันคิดว่าคุณไม่ได้พูดถึงในคำตอบของคุณฉันเพิ่งค้นพบเมื่อสองสามชั่วโมงก่อน สิ่งที่พวกเขาพูดถึงในที่นี้ดูเหมือนจะสำคัญ แต่ก็ยังไม่ชัดเจนเพียงพอสำหรับฉัน stackoverflow.com/questions/14517535/… (คุณสามารถอัปเดตได้หากต้องการฉันจะขอบคุณจริงๆ แต่อย่ารู้สึกว่าเป็นภาระผูกพันตอนนี้คำตอบของคุณดีพอ)
Gepser

@Gepser: เข้าใจแล้ว โดยย่อคือ: หากคุณต้องการnpmติดตั้งสคริปต์ต้นทาง Node.js เป็นCLI (อาจมีให้ใช้งานทั่วโลก) คุณต้องใช้บรรทัด shebang และnpmจะทำให้สามารถใช้งานได้บน Windows ดูคำตอบที่อัปเดตอีกครั้งของฉัน
mklement0

"ลักษณะของคำที่ทำให้การค้นหาไม่ใช่เรื่องง่าย" - คุณอาจต้องการลองduckduckgo.comสำหรับกรณีการใช้การค้นหาที่เฉพาะเจาะจงนี้
Ricardo

คำตอบ:


147

#!/usr/bin/env nodeเป็นตัวอย่างของบรรทัด shebang : บรรทัดแรกในไฟล์ข้อความธรรมดาที่เรียกใช้งานได้บนแพลตฟอร์มแบบ Unixที่บอกระบบว่าล่ามจะส่งไฟล์นั้นไปเพื่อการดำเนินการใดผ่านทางบรรทัดคำสั่งตาม#!คำนำหน้าเวทย์มนตร์(เรียกว่าshebang ) .

หมายเหตุ: ของ Windowsไม่ไม่สนับสนุนสาย shebangดังนั้นพวกเขากำลังได้อย่างมีประสิทธิภาพไม่สนใจที่นั่น บน Windows เป็นเพียงนามสกุลไฟล์ของไฟล์ที่กำหนดว่าไฟล์ปฏิบัติการจะแปลว่าอะไร แต่คุณยังต้องการพวกเขาในบริบทของ npm[1]

การสนทนาทั่วไปของสาย Shebangต่อไปนี้จำกัด เฉพาะแพลตฟอร์มที่คล้าย Unix:

ในการอภิปรายต่อไปนี้ผมจะคิดว่าไฟล์ที่มีรหัสที่มาสำหรับการดำเนินการโดย Node.js fileเป็นชื่อเพียง

  • คุณต้องการบรรทัดนี้หากคุณต้องการเรียกใช้ซอร์สไฟล์ Node.js โดยตรงเป็นไฟล์ปฏิบัติการในสิทธิ์ของมันเอง - ถือว่าไฟล์ถูกทำเครื่องหมายว่าปฏิบัติการได้ด้วยคำสั่งเช่นchmod +x ./fileซึ่งจะช่วยให้คุณสามารถเรียกใช้ไฟล์ได้ ด้วยตัวอย่างเช่น./fileหรือถ้ามันอยู่ในหนึ่งในไดเรกทอรีที่ระบุไว้ในตัวแปรเป็นเพียง$PATHfile

    • โดยเฉพาะคุณต้องบรรทัด shebang เพื่อสร้างCLIsขึ้นอยู่กับไฟล์ที่มา Node.js เป็นส่วนหนึ่งของ NPM แพคเกจด้วย CLI (s) จะได้รับการติดตั้งโดยnpmขึ้นอยู่กับมูลค่าของ"bin"สำคัญในแพคเกจของpackage.jsonไฟล์ ; ดูคำตอบนี้สำหรับวิธีการทำงานกับแพ็คเกจที่ติดตั้งทั่วโลก เชิงอรรถ [1] แสดงให้เห็นว่าสิ่งนี้จัดการอย่างไรบน Windows
  • คุณไม่จำเป็นต้องใช้บรรทัดนี้เพื่อเรียกใช้ไฟล์อย่างชัดเจนผ่านnodeล่ามเช่นnode ./file


ข้อมูลพื้นฐานเพิ่มเติม :

#!/usr/bin/env <executableName>เป็นวิธีการระบุล่ามแบบพกพา : สรุปสั้น ๆ ว่า: ดำเนินการ<executableName>ทุกที่ที่คุณ (ครั้งแรก) พบในไดเร็กทอรีที่ระบุไว้ใน$PATHตัวแปร (และโดยปริยายส่งพา ธ ไปยังไฟล์ที่อยู่ในมือ)

สิ่งนี้อธิบายถึงความจริงที่ว่าล่ามที่ระบุอาจถูกติดตั้งในตำแหน่งที่ตั้งที่แตกต่างกันในแพลตฟอร์มต่างๆซึ่งเป็นกรณีnodeของไบนารี Node.js

ในทางตรงกันข้ามตำแหน่งของenvยูทิลิตี้เองสามารถพึ่งพาได้ว่าอยู่ในตำแหน่งเดียวกันบนแพลตฟอร์มกล่าวคือ/usr/bin/env- และการระบุพา ธแบบเต็มไปยังไฟล์ปฏิบัติการนั้นจำเป็นต้องใช้ในบรรทัด shebang

โปรดทราบว่ายูทิลิตี้ POSIX envถูกนำมาใช้ใหม่ที่นี่เพื่อค้นหาตามชื่อไฟล์และเรียกใช้ไฟล์ปฏิบัติการในไฟล์$PATH.
จุดประสงค์ที่แท้จริงของenvคือการจัดการสภาพแวดล้อมสำหรับคำสั่ง - ดูenv's POSIX ข้อมูลจำเพาะและคำตอบที่เป็นประโยชน์คี ธ ธ อมป์สัน


นอกจากนี้ยังเป็นที่น่าสังเกตว่า Node.js กำลังสร้างข้อยกเว้นทางไวยากรณ์สำหรับบรรทัด shebang เนื่องจากไม่ใช่รหัส JavaScript ที่ถูกต้อง ( #ไม่ใช่อักขระข้อคิดเห็นใน JavaScript ซึ่งแตกต่างจากเชลล์แบบ POSIX และตัวแปลอื่น ๆ )


[1] เพื่อประโยชน์ของความสอดคล้องข้ามแพลตฟอร์มnpmสร้างไฟล์Wrapper *.cmd (ไฟล์แบตช์) บน Windowsเมื่อติดตั้งไฟล์ปฏิบัติการที่ระบุในpackage.jsonไฟล์ของแพ็คเกจ(ผ่าน"bin"คุณสมบัติ) โดยพื้นฐานแล้วไฟล์แบตช์ของ wrapper เหล่านี้เลียนแบบการทำงานของ Unix shebang: พวกมันเรียกไฟล์เป้าหมายอย่างชัดเจนพร้อมกับไฟล์ปฏิบัติการที่ระบุในบรรทัด shebangดังนั้นสคริปต์ของคุณจะต้องมีบรรทัด shebang แม้ว่าคุณจะตั้งใจที่จะรันบน Windows ก็ตาม - ดูคำตอบนี้ของฉันสำหรับรายละเอียด
เนื่องจาก*.cmdสามารถเรียกใช้ไฟล์ได้โดยไม่ต้องใช้.cmdสิ่งนี้ทำให้ประสบการณ์ข้ามแพลตฟอร์มที่ราบรื่น: ทั้งบน Windows และ Unix คุณสามารถเรียกใช้npmCLI ที่ติดตั้งได้อย่างมีประสิทธิภาพโดยใช้ชื่อเดิมที่ไม่มีส่วนขยาย


คุณสามารถให้คำอธิบายหรือสรุปสำหรับหุ่นอย่างฉันได้หรือไม่?
Andrew Lam

4
@AndrewLam: ใน Windows นามสกุลไฟล์เช่น.cmdและ.pyกำหนดว่าจะใช้โปรแกรมอะไรในการเรียกใช้ไฟล์ดังกล่าว ใน Unix บรรทัด shebang จะทำหน้าที่นั้น ในการnpmทำงานบนแพลตฟอร์มที่รองรับทั้งหมดคุณต้องมีสาย shebang แม้กระทั่งบน Windows
mklement0

28

สคริปต์ที่จะดำเนินการโดยล่ามโดยปกติจะมีบรรทัด Shebangที่ด้านบนเพื่อบอกระบบปฏิบัติการว่าจะดำเนินการอย่างไร

หากคุณมีสคริปต์ที่ชื่อfooซึ่งมีบรรทัดแรก#!/bin/shระบบจะอ่านบรรทัดแรกนั้นและดำเนินการเทียบเท่ากับ/bin/sh foo. ด้วยเหตุนี้ล่ามส่วนใหญ่จึงถูกตั้งค่าให้ยอมรับชื่อของไฟล์สคริปต์เป็นอาร์กิวเมนต์บรรทัดคำสั่ง

ชื่อล่ามที่ตามหลัง#!จะต้องเป็นเส้นทางแบบเต็ม ระบบปฏิบัติการจะไม่ค้นหา$PATHล่ามของคุณเพื่อค้นหาล่าม

หากคุณมีสคริปต์ที่ต้องดำเนินnodeการวิธีที่ชัดเจนในการเขียนบรรทัดแรกคือ:

#!/usr/bin/node

แต่จะไม่ได้ผลหากnodeไม่ได้ติดตั้งคำสั่งใน/usr/bin.

วิธีแก้ปัญหาทั่วไปคือการใช้envคำสั่ง (ซึ่งไม่ได้มีไว้เพื่อจุดประสงค์นี้จริงๆ ):

#!/usr/bin/env node

หากสคริปต์ของคุณถูกเรียกfooระบบปฏิบัติการจะเทียบเท่ากับไฟล์

/usr/bin/env node foo

envคำสั่งรันคำสั่งอื่นที่มีชื่อจะได้รับในบรรทัดคำสั่งผ่านข้อโต้แย้งใดต่อไปนี้คำสั่งว่า เหตุผลที่ใช้ที่นี่คือenvจะค้นหา$PATHคำสั่ง ดังนั้นหากnodeมีการติดตั้งใน/usr/local/bin/nodeและคุณมี/usr/local/binในของคุณ$PATHที่คำสั่งจะเรียกenv/usr/local/bin/node foo

วัตถุประสงค์หลักของenvคำสั่งคือการรันคำสั่งอื่นด้วยสภาพแวดล้อมที่แก้ไขเพิ่มหรือลบตัวแปรสภาพแวดล้อมที่ระบุก่อนรันคำสั่ง แต่ไม่มีอาร์กิวเมนต์เพิ่มเติมเพียงแค่รันคำสั่งด้วยสภาพแวดล้อมที่ไม่เปลี่ยนแปลงซึ่งเป็นสิ่งที่คุณต้องการในกรณีนี้

แนวทางนี้มีข้อบกพร่องบางประการ ระบบที่เหมือน Unix สมัยใหม่ส่วนใหญ่มี/usr/bin/envแต่ฉันทำงานบนระบบรุ่นเก่าที่มีการenvติดตั้งคำสั่งในไดเรกทอรีอื่น อาจมีข้อ จำกัด เกี่ยวกับอาร์กิวเมนต์เพิ่มเติมที่คุณสามารถส่งผ่านโดยใช้กลไกนี้ หากผู้ใช้ไม่มีไดเร็กทอรีที่มีnodeคำสั่งอยู่$PATHหรือมีการเรียกใช้คำสั่งnodeอื่นอาจเรียกใช้คำสั่งที่ไม่ถูกต้องหรือใช้งานไม่ได้เลย

แนวทางอื่น ๆ ได้แก่ :

  • ใช้#!บรรทัดที่ระบุพา ธ แบบเต็มไปยังnodeคำสั่งเองอัพเดตสคริปต์ตามความจำเป็นสำหรับระบบต่างๆ หรือ
  • เรียกใช้nodeคำสั่งด้วยสคริปต์ของคุณเป็นอาร์กิวเมนต์

ดูคำถามนี้ (และคำตอบของฉัน ) เพื่อดู#!/usr/bin/envเคล็ดลับเพิ่มเติม

อนึ่งในระบบของฉัน (Linux Mint 17.2) ติดตั้งเป็น/usr/bin/nodejsไฟล์. ตามบันทึกของฉันมันเปลี่ยนจาก/usr/bin/nodeเป็น/usr/bin/nodejsระหว่าง Ubuntu 12.04 และ 12.10 #!/usr/bin/envเคล็ดลับจะไม่ได้ความช่วยเหลือเกี่ยวกับที่ (ยกเว้นกรณีที่คุณตั้งค่า symlink หรือสิ่งที่คล้ายกัน)

UPDATE: ความคิดเห็นโดย mtraceur กล่าวว่า (ฟอร์แมตใหม่):

วิธีแก้ปัญหาสำหรับปัญหา nodejs vs node คือการเริ่มต้นไฟล์ด้วยหกบรรทัดต่อไปนี้:

#!/bin/sh -
':' /*-
test1=$(nodejs --version 2>&1) && exec nodejs "$0" "$@"
test2=$(node --version 2>&1) && exec node "$0" "$@"
exec printf '%s\n' "$test1" "$test2" 1>&2
*/

ขั้นแรกจะเป็นการลองnodejsแล้วจึงลองnodeและพิมพ์เฉพาะข้อความแสดงข้อผิดพลาดหากไม่พบทั้งสองข้อ คำอธิบายอยู่นอกขอบเขตของความคิดเห็นเหล่านี้ฉันแค่ทิ้งไว้ที่นี่เผื่อว่าจะช่วยให้ทุกคนจัดการกับปัญหาได้เนื่องจากคำตอบนี้ทำให้ปัญหาเกิดขึ้น

ฉันไม่ได้ใช้ NodeJS เมื่อเร็ว ๆ นี้ ความหวังของฉันคือปัญหาnodejsกับnodeปัญหาได้รับการแก้ไขแล้วในช่วงหลายปีที่ผ่านมาตั้งแต่ฉันโพสต์คำตอบนี้ครั้งแรก บน Ubuntu 18.04 nodejsแพคเกจจะติดตั้ง/usr/bin/nodejsเป็น symlink ไปยัง/usr/bin/node. ในระบบปฏิบัติการรุ่นก่อนหน้า (Ubuntu หรือ Linux Mint ฉันไม่แน่ใจว่าอันไหน) มีnodejs-legacyแพ็คเกจที่ให้nodeเป็น symlink nodejsไม่รับประกันว่าฉันมีรายละเอียดถูกต้องทั้งหมด


คำตอบที่ละเอียดถี่ถ้วนมากว่าทำไมสิ่งต่างๆ
Suraj Jain

1
วิธีแก้ปัญหาสำหรับnodejsVS nodeปัญหาคือการเริ่มต้นไฟล์ที่มีดังต่อไปนี้หกสาย 1) #!/bin/sh -2) ':' /*-3) test1=$(nodejs --version 2>&1) && exec nodejs "$0" "$@"4) test2=$(node --version 2>&1) && exec node "$0" "$@"5) 6)exec printf '%s\n' "$test1" "$test2" 1>&2 */ขั้นแรกจะเป็นการลองnodejsแล้วจึงลองnodeและพิมพ์เฉพาะข้อความแสดงข้อผิดพลาดหากไม่พบทั้งสองข้อ คำอธิบายอยู่นอกขอบเขตของความคิดเห็นเหล่านี้ฉันแค่ทิ้งไว้ที่นี่เผื่อว่าจะช่วยให้ทุกคนจัดการกับปัญหาได้เนื่องจากคำตอบนี้ทำให้ปัญหาเกิดขึ้น
mtraceur

@mtraceur: ฉันได้รวมความคิดเห็นของคุณไว้ในคำตอบของฉัน ทำไมต้อง-เป็น#!สาย?
Keith Thompson

-ในเป็นเพียงนิสัยที่ทำให้แน่ใจว่าจะทำงานเปลือกที่เหมาะสมในแคบมากและไม่น่าชุดของสถานการณ์ว่าชื่อสคริปต์หรือทางญาติว่าเปลือกเห็นเริ่มต้นด้วยการเป็น#!/bin/sh - -(นอกจากนี้ใช่ดูเหมือนว่า distro หลักทุกตัวจะกลับมาnodeเป็นชื่อหลักฉันไม่ได้ไปขุดเพื่อตรวจสอบเมื่อแสดงความคิดเห็น แต่เท่าที่ฉันรู้มีเพียงแผนผังตระกูล Debian distro เท่านั้นที่ใช้nodejsและดูเหมือนว่า เหมือนที่พวกเขาเปลี่ยนกลับไปสนับสนุนnodeเมื่อ Debian ทำ)
mtraceur

ในทางเทคนิคเส้นประเดียวเป็นอาร์กิวเมนต์แรกไม่ได้หมายถึง "จุดสิ้นสุดของตัวเลือก" แต่เดิมหมายถึง "ปิด-xและ-v" แต่เนื่องจากการชอบแบบบอร์นในช่วงต้นจะแยกวิเคราะห์อาร์กิวเมนต์แรกเป็นตัวเลือกที่เป็นไปได้เท่านั้นและเนื่องจากเชลล์เริ่มต้นด้วยตัวเลือกเหล่านั้นปิด เป็นเรื่องที่ไม่เหมาะสมที่จะทำให้เชลล์ไม่พยายามแยกวิเคราะห์ชื่อสคริปต์ตั้งแต่ดั้งเดิมและยังคงใช้ไม่ได้เนื่องจากพฤติกรรมยังคงอยู่ในรูปแบบบอร์นสมัยใหม่ด้วยเหตุผลด้านความเข้ากันได้ ถ้าฉันจำประวัติบอร์นและเรื่องไม่สำคัญเกี่ยวกับการพกพาทั้งหมดได้
mtraceur

0

คำตอบสั้น ๆ : เป็นเส้นทางไปสู่ล่าม

แก้ไข (คำตอบแบบยาว): สาเหตุที่ไม่มีเครื่องหมายทับก่อน "โหนด" เนื่องจากคุณไม่สามารถรับประกันความน่าเชื่อถือของ #! / bin / ได้เสมอไป บิต "/ env" ทำให้โปรแกรมข้ามแพลตฟอร์มได้มากขึ้นโดยการรันสคริปต์ในสภาพแวดล้อมที่ปรับเปลี่ยนและความสามารถในการค้นหาโปรแกรมล่ามได้อย่างน่าเชื่อถือมากขึ้น

คุณไม่จำเป็นต้องใช้ แต่ควรใช้เพื่อให้แน่ใจว่าพกพาได้ (และเป็นมืออาชีพ)


1
/usr/bin/envบิตไม่ได้ปรับเปลี่ยนสภาพแวดล้อม เป็นเพียงคำสั่งในตำแหน่งที่รู้จัก (ส่วนใหญ่) ซึ่งเรียกใช้คำสั่งอื่นที่กำหนดให้เป็นอาร์กิวเมนต์และค้นหา$PATHเพื่อค้นหา ประเด็นคือ#!บรรทัดต้องการพา ธ แบบเต็มไปยังคำสั่งที่ถูกเรียกและคุณไม่จำเป็นต้องรู้ว่าnodeติดตั้งที่ไหน
Keith Thompson

นั่นคือสิ่งที่ฉันกำลังจะไปขอบคุณที่ชี้แจง!
Quantum
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.