Universal Node.js shebang?


42

Node.jsเป็นที่นิยมมากในทุกวันนี้และฉันได้เขียนสคริปต์ลงไป น่าเสียดายที่ความเข้ากันได้เป็นปัญหา อย่างเป็นทางการล่าม Node.js ควรจะเรียกว่าnodeแต่ Debian และ Ubuntu จัดส่งปฏิบัติการที่เรียกว่าnodejsแทน

ฉันต้องการสคริปต์แบบพกพาที่ Node.js สามารถทำงานกับในสถานการณ์ให้ได้มากที่สุด สมมติว่าชื่อไฟล์คือfoo.jsฉันต้องการให้สคริปต์ทำงานในสองวิธี:

  1. ./foo.jsเรียกใช้สคริปต์ถ้าอย่างใดอย่างหนึ่งnodeหรืออยู่ในnodejs$PATH
  2. node foo.jsยังเรียกใช้สคริปต์ (สมมติว่าล่ามเรียกว่าnode)

หมายเหตุ:คำตอบโดย xavierm02 และตัวฉันเป็นสองรูปแบบของสคริปต์หลายภาษา ฉันยังคงสนใจวิธีการแก้ปัญหาแบบ shebang อย่างแท้จริงหากมีอยู่จริง


ฉันคิดว่าไม่มีวิธีแก้ปัญหาที่แท้จริงสำหรับเรื่องนี้เนื่องจากเป็นระบบที่ได้รับอนุญาตให้ตั้งชื่อไฟล์เรียกทำงานที่สร้างขึ้นเองตามอำเภอใจโดยระบบบิลด์ ไม่มีอะไรที่ป้องกันคุณจากการตั้งชื่อตัวแปลภาษาไพ ธ อนอัลฟาเซนโทริคุณแค่ทำตามอนุสัญญาและตั้งชื่อมันว่าไพ ธ อน ฉันขอแนะนำให้ใช้ชื่อมาตรฐานnodeสำหรับสคริปต์ของคุณหรือสร้างสคริปต์ที่ปรับเปลี่ยน shebang

@ G.Kayaalp การเมืองและการประชุมกันมีผู้ใช้ Debian / Ubuntu / Fedora จำนวนมากและฉันต้องการสร้างสคริปต์ที่เหมาะกับพวกเขา ฉันไม่ต้องการตั้งค่าระบบ build สำหรับสิ่งนี้ (ใครก็ตามที่สร้างเชลล์สคริปต์ก่อนที่จะรันพวกเขา?) และฉันไม่ต้องการให้การสนับสนุนalphacentauriเช่นนั้น หากมีการเรียกใช้nodejsงานได้คุณสามารถมั่นใจได้ 99% ว่าเป็น Node.js ทำไมไม่สนับสนุนทั้งสองnodejsและnode?
dancek

ติดตั้งแพ็กเกจ nodejs-legacy เหตุผลที่จำเป็นคือชื่อนี้หยิ่งยะโสมากเกินไปบางคนมีชื่อก่อน อย่างไรก็ตามแพ็คเกจอื่นนั้นยินดีที่จะแบ่งปันชื่อ
user3710044

คำตอบ:


54

สิ่งที่ดีที่สุดที่ฉันคิดไว้คือ "สองแถวชีท" ซึ่งเป็นสคริปต์ที่พูดได้หลายภาษา

#!/bin/sh
':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"

console.log('Hello world!');

บรรทัดแรกคือเปลือกของ Bourne Shebang Node.js ข้าม shebang ที่พบดังนั้นนี่เป็นไฟล์จาวาสคริปต์ที่ถูกต้องเท่าที่ Node.js เกี่ยวข้อง

บรรทัดที่สองเรียกเชลล์ no-op :พร้อมอาร์กิวเมนต์//จากนั้นเรียกใช้งานnodejsหรือnodeด้วยชื่อของไฟล์นี้เป็นพารามิเตอร์ command -vใช้แทนwhichการพกพา ไวยากรณ์การแทนที่คำสั่ง$(...)ไม่ใช่ Bourne อย่างเคร่งครัดดังนั้นโปรดเลือก backticks หากคุณเรียกใช้ในช่วงทศวรรษ 1980

Node.js เพียงแค่ประเมินสตริง':'ซึ่งเป็นเหมือนไม่มีการ op และส่วนที่เหลือของบรรทัดจะแยกเป็นความคิดเห็น

ส่วนที่เหลือของไฟล์เป็นเพียงจาวาสคริปต์เก่าธรรมดา เชลล์ย่อยจะหยุดทำงานหลังจากexecบรรทัดที่สองเสร็จสมบูรณ์ดังนั้นเชลล์ที่เหลือจะไม่ถูกอ่านโดยเชลล์

ขอบคุณ xavierm02 สำหรับแรงบันดาลใจและผู้แสดงความคิดเห็นสำหรับข้อมูลเพิ่มเติม!


1
ทางออกที่ดี อีกทางเลือกหนึ่งของ':'วิธีการคือการใช้// 2>/dev/null(ซึ่งเป็นสิ่งที่nvmทำ): เพื่อทุบตีมันเป็นข้อผิดพลาด ( bash: //: Is a directory) ซึ่งการเปลี่ยนเส้นทาง2>/dev/nullจะละเว้นอย่างเงียบ ๆ เพื่อ JavaScript ทั้งบรรทัดกลายเป็นความคิดเห็น นอกจากนี้ - และฉันไม่คาดหวังว่านี่จะเป็นปัญหา IRL - commandมีมุมแหลมที่มันยังรายงานฟังก์ชั่นของเชลล์และชื่อแทนด้วย-v- แม้ว่ามันจะไม่เรียกใช้พวกมัน ดังนั้นหากคุณมีฟังก์ชั่นการส่งออกเชลล์หรือชื่อแทน (สมมติว่าshopt -s expand_aliases) ชื่อnodeหรือnodejsสิ่งต่าง ๆ อาจแตก
mklement0

1
POSIX sh เป็นที่แพร่หลายในทุกวันนี้ (ยกเว้น Solaris / bin / sh - แต่ Solaris มาพร้อมกับ AT&T ksh ออกจากกล่องซึ่งรองรับ POSIX) และMarkus Kuhn แนะนำ (โดยมีเหตุผล) เพื่อปิดมัน IMHO คุณไม่ต้องการมัน แต่ใช่ว่ามันเพียงพอสำหรับmksh, bash, dashและเปลือกหอยอื่น ๆ ในปัจจุบัน Unix และ GNU ระบบ
mirabilos

1
ทางออกที่ดี; แม้ว่าพกพามากขึ้นจะใช้#!/usr/bin/env shแทน#!/bin/sh(ต่อen.wikipedia.org/wiki/Shebang_%28Unix%29#Portability )
user7089

2
ฉันจะระมัดระวังเกี่ยวกับการโฆษณา#!/usr/bin/env shว่า "พกพามากขึ้น" บทความ Wikipedia เดียวกันนั้นกล่าวว่า "วิธีนี้ใช้งานได้เป็นส่วนใหญ่เพราะพา ธ / usr / bin / env ใช้สำหรับยูทิลิตี env ... " นั่นไม่ใช่การรับรองที่ยอดเยี่ยมและฉันเดาว่าคุณจะเจอกับระบบโดยไม่ต้อง/usr/bin/envบ่อยกว่า คุณพบกับระบบที่ไม่มี/bin/shถ้ามีความแตกต่างของความถี่เลย
sheldonh

1
@dancek สวัสดีจากปี 2018 จะไม่เป็น//bin/sh -c :; exec ...รุ่นที่ทำความสะอาดของเส้นที่สอง?
har-wradim

10
#!/bin/sh
//bin/false || `which node || which nodejs` << `tail -n +2 $0`
console.log('ok');

//bin/falseเป็นสิ่งเดียวกัน/bin/falseยกเว้นว่าเครื่องหมายทับสองเปลี่ยนเป็นข้อคิดเห็นสำหรับโหนดและนั่นคือสาเหตุที่นี่ จากนั้นทางด้านขวาของอันแรก||จะถูกประเมิน 'which node || which nodejs'ด้วย backquotes แทนที่จะเป็นอัญประกาศเปิดตัวโหนดและ<<ฟีดมันสิ่งที่อยู่ทางด้านขวา ฉันสามารถใช้ตัวคั่นที่เริ่มต้นด้วย//เช่น dancek ทำมันจะทำงานได้ แต่ฉันคิดว่ามันสะอาดกว่าที่จะมีเพียงสองบรรทัดในตอนเริ่มต้นดังนั้นฉันจึงเคยtail -n +2 $0อ่านไฟล์เองยกเว้นสองบรรทัดแรก

และถ้าคุณเรียกใช้ในโหนดบรรทัดแรกจะถูกจดจำเป็น Shebang และละเว้น

(เห็นได้ชัดว่า sed สามารถใช้เพื่อแทนที่เนื้อหาไฟล์หางพิมพ์โดยไม่มีบรรทัดแรกและบรรทัดสุดท้าย )


ตอบก่อนแก้ไข:

#!/bin/sh
`which node || which nodejs` <<__HERE__
console.log('ok');
__HERE__

#!/bin/shคุณไม่สามารถทำในสิ่งที่คุณต้องการเพื่อให้สิ่งที่คุณทำแทนที่จะมีการเรียกใช้สคริปต์เชลล์จึง which node || which nodejsเชลล์สคริปต์ที่จะได้รับเส้นทางของแฟ้มที่จำเป็นในการดำเนินการโหนดที่เป็น backquotes อยู่ที่นี่เพื่อที่จะได้รับการดำเนินการดังนั้น'which node || which nodejs'(ด้วย backquotes แทนเครื่องหมายคำพูด) เพียงแค่เรียกโหนด <<จากนั้นคุณก็กินสคริปต์ของคุณไปด้วย __HERE__เป็นตัวคั่นสคริปต์กับคุณ และconsole.log('ok');เป็นตัวอย่างของสคริปต์ที่คุณควรแทนที่ด้วยสคริปต์ของคุณ


คำอธิบายจะดี
0xC0000022L

ดีกว่าพูดตัวคั่นที่นี่เอกสารที่จะหลีกเลี่ยงการขยายตัวพารามิเตอร์แทนคำสั่งและการขยายตัวทางคณิตศาสตร์ที่จะดำเนินการในรหัส JavaScript <<'__HERE__'ก่อนที่จะดำเนินการ:
จัดการ

สิ่งนี้กำลังได้รับความสนใจ! แม้ว่าจะ//bin/falseไม่ได้ทำงานในสภาพแวดล้อม MSYS ของฉันและฉันต้องการคำพูดรอบ backticks เป็นของฉันอยู่ที่node C:\Program Files\...ใช่ฉันทำงานในสภาพแวดล้อมที่เลวร้าย ...
dancek

1
//bin/falseไม่ทำงานบน Mac OS X เช่นกัน ฉันขอโทษ แต่ดูเหมือนจะไม่สามารถพกพาได้อีกต่อไป
dancek

2
whichคำสั่งยังไม่ได้เป็นแบบพกพา
จอร์แดน

9

นี่เป็นเพียงปัญหาของระบบที่ใช้เดเบียนซึ่งนโยบายมีความหมายที่ชัดเจน

ฉันไม่รู้ว่า Fedora ให้เลขฐานสองเรียกว่า nodejs แต่ฉันไม่เคยเห็นเลย แพ็กเกจเรียกว่า nodejs และติดตั้งไบนารีที่เรียกว่าโหนด

เพียงใช้ symlink เพื่อใช้สามัญสำนึกกับระบบที่ใช้เดเบียนของคุณจากนั้นคุณสามารถใช้สติที่มีเหตุผล คนอื่นจะใช้ shebangs สติอยู่แล้วดังนั้นคุณจะต้องใช้ symlink นั้น

#!/usr/bin/env node

console.log("Spread the love.");

ฉันขอโทษ แต่นี่ไม่ได้ตอบคำถาม ฉันเข้าใจมุมมองทางการเมือง แต่นั่นไม่ใช่ประเด็นที่นี่
dancek

สำหรับบันทึกระบบ Fedora บางระบบมีความnodejsสามารถในการปฏิบัติการแต่นั่นไม่ใช่ความผิดของ Fedora ขออภัยในความจริงที่ไม่ถูกต้อง
dancek

8
ไม่จำเป็นต้องขอโทษ คุณพูดถูก คำตอบของฉันไม่ตอบคำถามของคุณ มันพูดถึงคำถามของคุณโดยแนะนำว่าคำถามนั้น จำกัด ขอบเขตให้กับโซลูชันที่มีประโยชน์น้อยกว่าที่มี ฉันเสนอคำแนะนำเชิงปฏิบัติที่ได้รับการบอกเล่าจากประวัติศาสตร์ โหนดไม่ใช่ล่ามตัวแรกที่พบที่นี่ คุณควรจะได้เห็นความวุ่นวายที่นำไปสู่ Larry กำแพงประกาศว่า shebang Perl #!/usr/bin/perlจะต้อง ที่กล่าวว่าคุณไม่จำเป็นต้องชอบหรือใช้คำแนะนำของฉัน ความสงบ.
เชลดอน

3

หากคุณไม่สนใจที่จะสร้าง.shไฟล์เล็ก ๆ น้อย ๆฉันก็มีทางออกสำหรับคุณเล็กน้อย คุณสามารถสร้างเชลล์สคริปต์เล็กน้อยเพื่อกำหนดว่าโหนดใดที่สามารถใช้งานได้และใช้สคริปต์นี้ใน shebang ของคุณ:

shebang.sh :

#!/bin/sh
`which node || which nodejs` $@

script.js :

#!./shebang.sh
console.log('hello');

./script.jsทำเครื่องหมายทั้งปฏิบัติและการทำงาน

วิธีนี้คุณหลีกเลี่ยงการเขียนสคริปต์หลายภาษา ฉันไม่คิดว่าการใช้หลาย ๆ บรรทัดเป็นไปได้แม้ว่ามันจะเป็นความคิดที่ดี

แม้ว่าวิธีนี้จะช่วยแก้ปัญหาในแบบที่คุณต้องการ แต่ดูเหมือนว่าไม่มีใครสนใจเรื่องนี้ ยกตัวอย่างเช่นuglifyjsและCoffeeScriptใช้#!/usr/bin/env node, NPMnodeใช้สคริปต์เปลือกเป็นจุดรายการอีกครั้งซึ่งเรียกปฏิบัติการอย่างชัดเจนที่มีชื่อ ฉันเป็นผู้ใช้อูบุนตูและฉันไม่รู้สิ่งนี้เพราะฉันรวบรวมโหนดเสมอ ฉันกำลังพิจารณาที่จะรายงานว่านี่เป็นข้อผิดพลาด


ฉันค่อนข้างแน่ใจว่าอย่างน้อยคุณจะต้องขอให้ผู้ใช้chmod +xในสคริปต์ดวลจุดโทษ ... ดังนั้นคุณอาจขอให้พวกเขาตั้งตัวแปรให้ตำแหน่งไปยังโหนดปฏิบัติการของพวกเขา ...
xavierm02

@ xavierm02 หนึ่งสามารถปล่อยสคริปต์chmod +x'd และผมเห็นว่าตัวแปร NODE which node || which nodejsจะดีกว่า #!/usr/bin/env nodeแต่ผู้ถามต้องการที่จะให้ประสบการณ์การออกจากกล่องแม้ว่าจะมีหลายโครงการที่สำคัญโหนดเพียงแค่การใช้งาน

1

เพื่อความสมบูรณ์ที่นี่เป็นอีกสองวิธีในการทำบรรทัดที่สอง:

// 2>/dev/null || echo yes
false //|| echo yes

แต่ไม่มีข้อได้เปรียบเหนือคำตอบที่เลือก:

':' //; || echo yes

นอกจากนี้หากคุณรู้ว่าจะพบสิ่งใดสิ่งหนึ่งnodeหรือnodejs(แต่ไม่ใช่ทั้งสองอย่าง) งานต่อไปนี้:

exec `command -v node nodejs` "$0" "$@"

แต่นั่นเป็น "ถ้า" ใหญ่ดังนั้นฉันคิดว่าคำตอบที่เลือกยังคงเป็นคำตอบที่ดีที่สุด


นอกจากนี้คุณสามารถเรียกใช้ใด ๆfoobar // 2>/dev/nullตราบใดที่foobarไม่ใช่คำสั่ง และยูทิลิตี้มากมายที่พบในระบบ POSIX ใด ๆสามารถเรียกใช้พร้อม//อาร์กิวเมนต์ได้
dancek

1

ฉันเข้าใจว่านี่ไม่ได้ตอบคำถาม แต่ฉันเชื่อว่าคำถามนั้นถูกถามด้วยหลักฐานเท็จ

อูบุนตูผิดที่นี่ เขียน shebang สากลสำหรับสคริปต์ของคุณเองจะไม่เปลี่ยนแพคเกจอื่น ๆ #!/usr/bin/env nodeที่คุณไม่สามารถควบคุมได้ซึ่งใช้มาตรฐานโดยพฤตินัยของ ระบบของคุณจะต้องให้nodeในPATHถ้ามันมีความประสงค์สำหรับสคริปต์การกำหนดเป้าหมาย nodejs วิ่งกับมัน

ตัวอย่างเช่นแม้แต่npmแพคเกจที่Ubuntu จัดไว้ให้จะเขียน Shebangs ในแพ็คเกจด้วย:

$ cd
$ rm -rf test_npm
$ mkdir test_npm
$ cd test_npm
$ npm install mkdirp 2>&1 | grep -ve home
`-- mkdirp@0.5.1
  `-- minimist@0.0.8

npm WARN test_npm No description
npm WARN test_npm No repository field.
npm WARN test_npm No README data
npm WARN test_npm No license field.
$ node_modules/.bin/mkdirp testdir
/usr/bin/env: 'node': No such file or directory
$ head -n1 node_modules/.bin/mkdirp
#!/usr/bin/env node
$ npm --version
3.5.2
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.