Chính xác thì “/ usr / bin / env node” làm gì ở đầu các tệp nút?


109

Tôi đã nhìn thấy dòng này #!/usr/bin/env nodeở đầu một số ví dụ trong nodejsvà tôi đã truy cập vào Google mà không tìm thấy bất kỳ chủ đề nào có thể trả lời lý do cho dòng đó.

Bản chất của các từ làm cho việc tìm kiếm không dễ dàng như vậy.

Tôi muốn đọc một số javascriptnodejscuốn sách gần đây và tôi không nhớ nhìn thấy nó trong bất kỳ trong số họ.

Nếu bạn muốn có một ví dụ, bạn có thể xem hướng dẫnRabbitMQ chính thức , họ có nó trong hầu hết các ví dụ của họ, đây là một trong số chúng:

#!/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);
});

Ai đó có thể giải thích cho tôi ý nghĩa của dòng này là gì?

Sự khác biệt nếu tôi đặt hoặc xóa dòng này là gì? Tôi cần nó trong những trường hợp nào?


3
Về cơ bản nó lấy môi trường của trình bao gọi và nhét môi trường đó vào bất kỳ ứng dụng nào được chỉ định. trong trường hợp này,node
Marc B

Thực ra là không, tôi không đến từ Windows, nhưng cảm ơn bạn đã cập nhật câu trả lời của mình. Tôi chỉ đang chờ xem có ai khác phản đối ý kiến ​​khác không. Chỉ có một điều mà tôi nghĩ rằng bạn đã không đề cập đến trong câu trả lời của mình, tôi chỉ tìm thấy nó một vài giờ trước. Những điều mà họ đề cập ở đây có vẻ quan trọng nhưng đối với tôi nó vẫn chưa đủ rõ ràng. stackoverflow.com/questions/14517535/… (bạn có thể cập nhật nếu muốn, tôi thực sự sẽ đánh giá cao điều đó, nhưng đừng cảm thấy nó giống như một nghĩa vụ, câu trả lời của bạn hiện tại là đủ tốt).
Gepser

@Gepser: Hiểu rồi. Tóm lại là: nếu bạn muốn npmcài đặt tập lệnh nguồn Node.js dưới dạng CLI (có thể khả dụng trên toàn cầu) , bạn phải sử dụng một dòng shebang - và npmthậm chí sẽ làm cho nó hoạt động trên Windows; xem câu trả lời cập nhật một lần nữa của tôi.
mklement0

"Bản chất của những lời làm cho tìm kiếm nó không phải là dễ dàng" - bạn có thể muốn thử duckduckgo.com cho trường hợp cụ thể sử dụng tìm kiếm này
Ricardo

Có thể có bản sao của Tại sao mọi người viết #! / Usr / bin / env python shebang trên dòng đầu tiên của tập lệnh Python? . Cùng một câu hỏi, phiên dịch viên khác nhau.
jww

Câu trả lời:


146

#!/usr/bin/env nodemột ví dụ của dòng shebang : dòng đầu tiên trong tệp văn bản thuần thực thi được trên các nền tảng giống Unix cho hệ thống biết trình thông dịch nào sẽ chuyển tệp đó đến để thực thi , thông qua dòng lệnh theo sau #!tiền tố ma thuật (được gọi là shebang ) .

Lưu ý: của Windows không không hỗ trợ dòng công việc , vì vậy họ đang có hiệu quả bị bỏ qua đó; trên Windows, nó chỉ là một phần mở rộng tên tệp nhất định xác định tệp thực thi nào sẽ diễn giải nó. Tuy nhiên, bạn vẫn cần chúng trong bối cảnhnpm . [1]

Sau đây, thảo luận chung về các dòng shebang được giới hạn cho các nền tảng giống Unix:

Trong phần thảo luận sau, tôi sẽ giả định rằng tệp chứa mã nguồn để Node.js thực thi được đặt tên đơn giản file.

  • Bạn CẦN dòng này , nếu bạn muốn gọi trực tiếp tệp nguồn Node.js , dưới dạng tệp thực thi theo đúng nghĩa của nó - điều này giả định rằng tệp đã được đánh dấu là có thể thực thi bằng một lệnh chẳng hạn chmod +x ./file, sau đó cho phép bạn gọi tệp với, ví dụ, ./filehoặc, nếu nó nằm trong một trong các thư mục được liệt kê trong $PATHbiến, đơn giản là file.

    • Cụ thể, bạn cần một dòng shebang để tạo CLI dựa trên tệp nguồn Node.js như một phần của gói npm , với (các) CLI sẽ được cài đặt npmdựa trên giá trị của "bin"khóa trong package.jsontệp của gói ; cũng xem câu trả lời này để biết cách hoạt động với các gói được cài đặt toàn cầu . Chú thích cuối trang [1] cho biết cách xử lý điều này trên Windows.
  • Bạn KHÔNG cần dòng này để gọi tệp một cách rõ ràng thông qua trình nodethông dịch, ví dụ:node ./file


Thông tin cơ bản tùy chọn :

#!/usr/bin/env <executableName>là một cách để portably xác định một thông dịch viên: trong Tóm lại, nó nói: thực hiện <executableName>mọi lúc mọi nơi (đầu tiên) tìm thấy nó trong các thư mục được liệt kê trong $PATHbiến (và ngầm vượt qua nó đường dẫn đến tập tin trong tầm tay).

Điều này giải thích cho thực tế là một trình thông dịch nhất định có thể được cài đặt ở các vị trí khác nhau trên các nền tảng, điều này chắc chắn là trường hợp nodecủa hệ nhị phân Node.js.

Ngược lại, bản thân vị trí của envtiện ích có thể được tin tưởng là ở cùng một vị trí trên các nền tảng, cụ thể là /usr/bin/env- và chỉ định đường dẫn đầy đủ đến tệp thực thi là bắt buộc trong một dòng shebang.

Lưu ý rằng tiện ích POSIX envđang được định vị lại ở đây để định vị theo tên tệp và thực thi một tệp thực thi trong $PATH.
Mục đích thực sự của envlà quản lý môi trường cho một lệnh - hãy xem envthông số kỹ thuật POSIX của lệnh và câu trả lời hữu ích của Keith Thompson .


Cũng cần lưu ý rằng Node.js đang tạo một ngoại lệ cú pháp cho các dòng shebang, vì chúng không phải là mã JavaScript hợp lệ ( #không phải là ký tự chú thích trong JavaScript, không giống như trong các trình bao giống POSIX và các trình thông dịch khác).


[1] Vì lợi ích của tính nhất quán trên nhiều nền tảng, hãy npmtạo tệp trình bao bọc *.cmd (tệp hàng loạt) trên Windows khi cài đặt các tệp thực thi được chỉ định trong package.jsontệp của gói (thông qua thuộc "bin"tính). Về cơ bản, các tệp hàng loạt của trình bao bọc này bắt chước chức năng shebang của Unix: chúng gọi tệp đích một cách rõ ràng với tệp thực thi được chỉ định trong dòng shebang - do đó, các tập lệnh của bạn phải bao gồm một dòng shebang ngay cả khi bạn chỉ định chạy chúng trên Windows - hãy xem câu trả lời này của tôi để biết chi tiết.
*.cmdcác tệp có thể được gọi mà không có.cmdtiện ích mở rộng, điều này tạo ra trải nghiệm đa nền tảng liền mạch: trên cả Windows và Unix, bạn có thể gọi một cách hiệu quả npmCLI được cài đặt theo tên ban đầu, không có tiện ích mở rộng.


Bạn có thể cung cấp lời giải thích hoặc tóm tắt cho những hình nộm như tôi không?
Andrew Lam

4
@AndrewLam: Trên Windows, phần mở rộng tên tệp chẳng hạn như .cmd.pyxác định chương trình nào sẽ được sử dụng để thực thi các tệp đó. Trên Unix, dòng shebang thực hiện chức năng đó. Để npmhoạt động trên tất cả các nền tảng được hỗ trợ, bạn cần dòng shebang ngay cả trên Windows.
mklement0

28

Các tập lệnh sẽ được thực thi bởi một trình thông dịch thường có một dòng shebang ở trên cùng để cho hệ điều hành biết cách thực thi chúng.

Nếu bạn có một tập lệnh được đặt tên foocó dòng đầu tiên #!/bin/sh, hệ thống sẽ đọc dòng đầu tiên đó và thực thi tương đương với /bin/sh foo. Do đó, hầu hết các trình thông dịch được thiết lập để chấp nhận tên của một tệp kịch bản như một đối số dòng lệnh.

Tên thông dịch viên theo sau #!phải là một đường dẫn đầy đủ; hệ điều hành sẽ không tìm kiếm của bạn $PATHđể tìm thông dịch viên.

Nếu bạn có một tập lệnh được thực thi bởi node, cách rõ ràng để viết dòng đầu tiên là:

#!/usr/bin/node

nhưng điều đó không hoạt động nếu nodelệnh không được cài đặt trong /usr/bin.

Một giải pháp phổ biến là sử dụng envlệnh (không thực sự dành cho mục đích này):

#!/usr/bin/env node

Nếu tập lệnh của bạn được gọi foo, hệ điều hành sẽ hoạt động tương đương với

/usr/bin/env node foo

Các envlệnh thực thi lệnh khác có tên được đưa ra trên dòng lệnh của nó, đi qua bất kỳ đối số sau đây để lệnh đó. Lý do nó được sử dụng ở đây là nó envsẽ tìm kiếm $PATHlệnh. Vì vậy, nếu nodeđược cài đặt trong /usr/local/bin/nodevà bạn có /usr/local/bintrong đó $PATH, envlệnh sẽ gọi /usr/local/bin/node foo.

Mục đích chính của envlệnh là thực hiện một lệnh khác với môi trường đã sửa đổi, thêm hoặc bớt các biến môi trường đã chỉ định trước khi chạy lệnh. Nhưng không có đối số bổ sung, nó chỉ thực hiện lệnh với một môi trường không thay đổi, đó là tất cả những gì bạn cần trong trường hợp này.

Có một số hạn chế đối với cách tiếp cận này. Hầu hết các hệ thống giống Unix hiện đại đều có /usr/bin/env, nhưng tôi đã làm việc trên các hệ thống cũ hơn nơi envlệnh được cài đặt trong một thư mục khác. Có thể có những hạn chế về các đối số bổ sung mà bạn có thể vượt qua bằng cách sử dụng cơ chế này. Nếu người dùng không có thư mục chứa nodelệnh $PATHhoặc có một số lệnh khác được gọi node, thì nó có thể gọi sai lệnh hoặc hoàn toàn không hoạt động.

Các cách tiếp cận khác là:

  • Sử dụng một #!dòng chỉ định đường dẫn đầy đủ đến nodechính lệnh đó, cập nhật tập lệnh nếu cần cho các hệ thống khác nhau; hoặc là
  • Gọi nodelệnh với tập lệnh của bạn làm đối số.

Xem thêm câu hỏi này (và câu trả lời của tôi ) để thảo luận thêm về #!/usr/bin/envthủ thuật.

Ngẫu nhiên, trên hệ thống của tôi (Linux Mint 17.2), nó được cài đặt dưới dạng /usr/bin/nodejs. Theo ghi chú của tôi, nó đã thay đổi từ /usr/bin/nodeđến /usr/bin/nodejsgiữa Ubuntu 12.04 và 12.10. Thủ #!/usr/bin/envthuật sẽ không giúp được gì (trừ khi bạn thiết lập một liên kết biểu tượng hoặc một cái gì đó tương tự).

CẬP NHẬT: Một bình luận của mtraceur nói (đã định dạng lại):

Cách giải quyết cho vấn đề nodejs vs node là bắt đầu tệp với sáu dòng sau:

#!/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
*/

Điều này đầu tiên sẽ thử nodejsvà sau đó thử nodevà chỉ in thông báo lỗi nếu cả hai đều không được tìm thấy. Lời giải thích nằm ngoài phạm vi của những nhận xét này, tôi chỉ để nó ở đây trong trường hợp nó giúp bất kỳ ai giải quyết vấn đề vì câu trả lời này đã đưa ra vấn đề.

Tôi đã không sử dụng NodeJS gần đây. Hy vọng của tôi là vấn đề nodejsvs. nodeđã được giải quyết trong những năm kể từ lần đầu tiên tôi đăng câu trả lời này. Trên Ubuntu 18.04, nodejsgói cài đặt /usr/bin/nodejsdưới dạng liên kết biểu tượng tới /usr/bin/node. Trên một số hệ điều hành trước đó (Ubuntu hoặc Linux Mint, tôi không chắc là cái nào), có một nodejs-legacygói được cung cấp nodedưới dạng liên kết tượng trưng nodejs. Không đảm bảo rằng tôi có tất cả các chi tiết đúng.


Một câu trả lời rất kỹ lưỡng cung cấp lý do tại sao của mọi thứ.
Suraj Jain

1
Một giải pháp cho vấn đề nodejsvs nodelà bắt đầu tệp với sáu dòng sau: 1) #!/bin/sh -, 2) ':' /*-3) test1=$(nodejs --version 2>&1) && exec nodejs "$0" "$@"4) test2=$(node --version 2>&1) && exec node "$0" "$@", 5) exec printf '%s\n' "$test1" "$test2" 1>&26) */. Điều này đầu tiên sẽ thử nodejsvà sau đó thử nodevà chỉ in thông báo lỗi nếu cả hai đều không được tìm thấy. Lời giải thích nằm ngoài phạm vi của những nhận xét này, tôi chỉ để nó ở đây trong trường hợp nó giúp bất kỳ ai giải quyết vấn đề vì câu trả lời này đã đưa ra vấn đề.
mtraceur

@mtraceur: Tôi đã kết hợp bình luận của bạn vào câu trả lời của mình. Tại sao -trên #!đường dây?
Keith Thompson

Các -trong #!/bin/sh -chỉ là một thói quen mà làm cho chắc chắn còn lại hoạt động vỏ ngay trong cực kỳ thu hẹp và khó tập các tình huống rằng tên kịch bản hoặc đường dẫn tương đối rằng vỏ thấy bắt đầu với một -. (Ngoài ra, vâng, có vẻ như mọi bản phân phối chính thống đã được tập hợp lại thành nodetên chính. Tôi đã không tìm hiểu kỹ khi đưa ra nhận xét của mình, nhưng theo như tôi biết thì chỉ có cây họ bản phân phối Debian được sử dụng nodejsvà có vẻ như như tất cả họ đều trở lại con người cũng hỗ trợ nodemột lần Debian đã làm).
mtraceur

Về mặt kỹ thuật, dấu gạch ngang đơn như đối số đầu tiên không có nghĩa là "kết thúc các tùy chọn" - ban đầu nó có nghĩa là "tắt -x-v", nhưng vì những người thích Bourne ban đầu chỉ phân tích cú pháp đối số đầu tiên là các tùy chọn có thể và vì trình bao bắt đầu với các tùy chọn đó tắt , nó có thể bị lạm dụng để khiến trình bao không cố gắng phân tích cú pháp tên tập lệnh kể từ bản gốc và do đó vẫn có thể lạm dụng vì hành vi được duy trì trong Bourne-likes hiện đại vì lý do tương thích. Nếu tôi nhớ tất cả các câu đố về lịch sử Bourne và tính di động của tôi đúng.
mtraceur

0

Câu trả lời ngắn gọn: Đó là con đường dẫn đến phiên dịch.

EDIT (Câu trả lời dài): Lý do không có dấu gạch chéo trước "nút" là vì bạn không thể luôn đảm bảo độ tin cậy của #! / Bin /. Bit "/ env" làm cho chương trình đa nền tảng hơn bằng cách chạy tập lệnh trong môi trường đã sửa đổi và khả năng tìm chương trình thông dịch đáng tin cậy hơn.

Bạn không nhất thiết phải cần nó, nhưng nên sử dụng để đảm bảo tính di động (và tính chuyên nghiệp)


1
Các /usr/bin/envchút không thay đổi môi trường. Nó chỉ là một lệnh tại một vị trí (hầu hết) đã biết gọi một lệnh khác được đưa ra dưới dạng đối số và tìm kiếm $PATHđể tìm nó. Vấn đề là #!dòng yêu cầu đường dẫn đầy đủ đến lệnh đang được gọi và bạn không nhất thiết phải biết nơi nodeđược cài đặt.
Keith Thompson

Đó là những gì tôi đã làm, cảm ơn vì đã làm rõ!
Lượng tử
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.