Apa sebenarnya yang dilakukan "/ usr / bin / env node" di awal file node?

109

Saya telah melihat baris ini #!/usr/bin/env nodedi awal beberapa contoh nodejsdan saya telah mencari di Google tanpa menemukan topik apa pun yang dapat menjawab alasan baris itu.

Sifat kata-katanya membuat pencarian tidak semudah itu.

Saya akan membaca beberapa javascriptdan nodejsbuku baru-baru ini dan saya tidak ingat melihat dalam salah satu dari mereka.

Jika Anda menginginkan contoh, Anda dapat melihat tutorialRabbitMQ resmi , mereka memilikinya di hampir semua contoh mereka, ini salah satunya:

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

Bisakah seseorang menjelaskan kepada saya apa arti baris ini?

Apa bedanya jika saya meletakkan atau menghapus baris ini? Dalam kasus apa saya membutuhkannya?

Gepser
sumber
3
itu pada dasarnya mengambil lingkungan shell pemanggil dan memasukkan lingkungan itu ke dalam aplikasi apa pun yang ditentukan. dalam hal ini,node
Marc B
Sebenarnya tidak, saya tidak keluar dari Windows, tapi terima kasih telah memperbarui jawaban Anda. Saya hanya menunggu untuk melihat apakah ada orang lain yang datang dengan pendapat yang berbeda. Hanya ada satu hal yang menurut saya tidak Anda sebutkan dalam jawaban Anda, saya baru menemukannya beberapa jam yang lalu. Hal-hal yang mereka sebutkan di sini sepertinya penting tapi belum cukup jelas bagi saya. stackoverflow.com/questions/14517535/… (Anda dapat memperbarui jika Anda mau, saya akan sangat menghargai itu, tetapi jangan merasa itu seperti kewajiban, jawaban Anda sudah cukup baik sekarang).
Gepser
@ Gepser: Mengerti. Singkatnya adalah: jika Anda ingin npmmenginstal skrip sumber Node.js sebagai CLI (berpotensi tersedia secara global) , Anda harus menggunakan baris shebang - dan npmbahkan akan membuatnya berfungsi di Windows; lihat jawaban saya yang sekali lagi diperbarui.
mklement0
"Sifat kata-kata membuat penelusuran tidak semudah itu" - Anda mungkin ingin mencoba duckduckgo.com untuk kasus penggunaan penelusuran khusus ini
Ricardo
Kemungkinan duplikat dari Mengapa orang menulis #! / Usr / bin / env python shebang pada baris pertama skrip Python? . Pertanyaan sama, penerjemah berbeda.
jww

Jawaban:

146

#!/usr/bin/env nodeadalah contoh dari baris shebang : baris pertama dalam file teks biasa yang dapat dieksekusi pada platform mirip Unix yang memberi tahu sistem penerjemah apa yang harus meneruskan file itu untuk dieksekusi , melalui baris perintah setelah #!awalan ajaib (disebut shebang ) .

Catatan: Windows tidak tidak mendukung garis peristiwa , sehingga mereka secara efektif diabaikan ada; pada Windows itu hanya ekstensi nama file yang ditentukan yang menentukan apa yang dapat dieksekusi akan menafsirkannya. Namun, Anda tetap membutuhkannya dalam konteksnpm . [1]

Berikut ini, diskusi umum tentang garis shebang terbatas pada platform mirip Unix:

Dalam diskusi berikut saya akan berasumsi bahwa file yang berisi kode sumber untuk dieksekusi oleh Node.js hanya diberi nama file.

  • Anda MEMBUTUHKAN baris ini , jika Anda ingin menjalankan file sumber Node.js secara langsung , sebagai file yang dapat dieksekusi dengan sendirinya - ini mengasumsikan bahwa file telah ditandai sebagai dapat dieksekusi dengan perintah seperti chmod +x ./file, yang kemudian memungkinkan Anda untuk memanggil file dengan, misalnya,, ./fileatau, jika terletak di salah satu direktori yang terdaftar dalam $PATHvariabel, cukup sebagai file.

    • Secara khusus, Anda memerlukan baris shebang untuk membuat CLI berdasarkan file sumber Node.js sebagai bagian dari paket npm , dengan CLI yang akan diinstal npmberdasarkan nilai "bin"kunci dalam package.jsonfile paket ; lihat juga jawaban ini untuk mengetahui cara kerjanya dengan paket yang diinstal secara global . Catatan kaki [1] menunjukkan bagaimana ini ditangani pada Windows.
  • Anda TIDAK memerlukan baris ini untuk memanggil file secara eksplisit melalui nodeinterpreter, misalnya,node ./file


Informasi latar belakang opsional :

#!/usr/bin/env <executableName>adalah cara portable menentukan penerjemah: Singkatnya, ia mengatakan: mengeksekusi <executableName>manapun Anda (pertama) menemukannya di antara direktori yang tercantum dalam $PATHvariabel (dan secara implisit lulus path ke file pada tangan).

Ini menjelaskan fakta bahwa interpreter tertentu dapat diinstal di lokasi berbeda di seluruh platform, yang pasti terjadi nodepada biner Node.js.

Sebaliknya, lokasi envutilitas itu sendiri dapat diandalkan untuk berada di lokasi yang sama di seluruh platform, yaitu /usr/bin/env- dan menentukan jalur lengkap ke dapat dieksekusi diperlukan dalam baris shebang.

Perhatikan bahwa utilitas POSIX envsedang digunakan ulang di sini untuk mencari berdasarkan nama file dan menjalankan file yang dapat dieksekusi di file $PATH.
Tujuan sebenarnya dari envadalah untuk mengelola lingkungan untuk sebuah perintah - lihat envspesifikasi POSIX dan jawaban bermanfaat Keith Thompson .


Perlu juga dicatat bahwa Node.js membuat pengecualian sintaks untuk baris shebang, karena baris tersebut bukan kode JavaScript yang valid ( #bukan karakter komentar dalam JavaScript, tidak seperti di shell seperti POSIX dan interpreter lainnya).


[1] Untuk kepentingan konsistensi lintas platform, npmmembuat file pembungkus *.cmd (file batch) di Windows saat menginstal file yang dapat dieksekusi yang ditentukan dalam package.jsonfile paket (melalui "bin"properti). Pada dasarnya, file batch pembungkus ini meniru fungsionalitas Shebang Unix: mereka memanggil file target secara eksplisit dengan eksekusi yang ditentukan di baris shebang - dengan demikian, skrip Anda harus menyertakan baris shebang bahkan jika Anda hanya bermaksud untuk menjalankannya di Windows - lihat jawaban ini milik saya untuk detailnya.
Karena *.cmdfile dapat dipanggil tanpa.cmdekstensi, ini membuat pengalaman lintas platform yang mulus: pada Windows dan Unix Anda dapat secara efektif menjalankan npmCLI yang terinstal dengan nama aslinya, tanpa ekstensi.

mklement0
sumber
Bisakah Anda memberikan penjelasan atau ringkasan untuk boneka seperti saya?
Andrew Lam
4
@AndrewLam: Pada Windows, ekstensi nama file seperti .cmddan .pymenentukan program apa yang akan digunakan untuk mengeksekusi file tersebut. Di Unix, garis shebang melakukan fungsi itu. Untuk membuat npmpekerjaan pada semua platform yang didukung, Anda memerlukan garis shebang bahkan di Windows.
mklement0
28

Skrip yang akan dieksekusi oleh penerjemah biasanya memiliki baris shebang di bagian atas untuk memberi tahu OS cara menjalankannya.

Jika Anda memiliki skrip bernama fooyang baris pertamanya adalah #!/bin/sh, sistem akan membaca baris pertama itu dan menjalankan yang setara dengan /bin/sh foo. Karena itu, sebagian besar penerjemah diatur untuk menerima nama file skrip sebagai argumen baris perintah.

Nama juru bahasa setelah #!harus jalur lengkap; OS tidak akan mencari Anda $PATHuntuk menemukan juru bahasa.

Jika Anda memiliki skrip untuk dieksekusi node, cara yang jelas untuk menulis baris pertama adalah:

#!/usr/bin/node

tetapi itu tidak berfungsi jika nodeperintah tidak diinstal di /usr/bin.

Sebuah solusi umum adalah dengan menggunakan envperintah (yang tidak benar-benar dimaksudkan untuk tujuan ini):

#!/usr/bin/env node

Jika skrip Anda dipanggil foo, OS akan melakukan hal yang sama dengan

/usr/bin/env node foo

The envperintah mengeksekusi perintah lain yang namanya diberikan pada baris perintah, melewati argumen berikut untuk perintah itu. Alasan digunakan di sini adalah untuk envmencari $PATHperintah. Jadi jika nodedipasang di /usr/local/bin/node, dan Anda memiliki /usr/local/bindi Anda $PATH, yang envperintah akan memanggil/usr/local/bin/node foo .

Tujuan utama dari envperintah ini adalah untuk menjalankan perintah lain dengan lingkungan yang dimodifikasi, menambahkan atau menghapus variabel lingkungan yang ditentukan sebelum menjalankan perintah. Tetapi tanpa argumen tambahan, itu hanya menjalankan perintah dengan lingkungan yang tidak berubah, yang Anda butuhkan dalam kasus ini.

Ada beberapa kelemahan dari pendekatan ini. Sebagian besar sistem mirip Unix modern memilikinya /usr/bin/env, tetapi saya bekerja pada sistem yang lebih lama tempat envperintah dipasang di direktori yang berbeda. Mungkin ada batasan pada argumen tambahan yang dapat Anda berikan menggunakan mekanisme ini. Jika pengguna tidak memiliki direktori yang berisi nodeperintah di $PATH, atau memiliki beberapa perintah berbeda yang dipanggil node, maka ia dapat menjalankan perintah yang salah atau tidak berfungsi sama sekali.

Pendekatan lainnya adalah:

  • Gunakan #!garis yang menentukan jalur lengkap kenode perintah itu sendiri, memperbarui skrip sesuai kebutuhan untuk sistem yang berbeda; atau
  • Panggil nodeperintah dengan skrip Anda sebagai argumen.

Lihat juga pertanyaan ini (dan jawaban saya ) untuk diskusi lebih lanjut tentang #!/usr/bin/envtriknya.

Kebetulan, di sistem saya (Linux Mint 17.2), itu diinstal sebagai /usr/bin/nodejs. Menurut catatan saya, itu berubah dari /usr/bin/nodeke /usr/bin/nodejsantara Ubuntu 12.04 dan 12.10. The #!/usr/bin/envtrick tidak akan membantu dengan itu (kecuali jika Anda menyiapkan symlink atau yang serupa).

UPDATE: Komentar oleh mtraceur mengatakan (diformat ulang):

Solusi untuk masalah nodejs vs node adalah memulai file dengan enam baris berikut:

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

Ini pertama-tama akan mencoba nodejsdan kemudian mencoba node, dan hanya mencetak pesan kesalahan jika keduanya tidak ditemukan. Penjelasan berada di luar cakupan komentar ini, saya hanya meninggalkannya di sini jika itu membantu siapa pun mengatasi masalah karena jawaban ini memunculkan masalah.

Saya belum pernah menggunakan NodeJS belakangan ini. Harapan saya adalah bahwa masalah nodejsvs. nodetelah diselesaikan selama bertahun-tahun sejak saya pertama kali memposting jawaban ini. Di Ubuntu 18.04, nodejspaket diinstal /usr/bin/nodejssebagai symlink ke /usr/bin/node. Pada beberapa OS sebelumnya (Ubuntu atau Linux Mint, saya tidak yakin yang mana), ada nodejs-legacypaket yang disediakan nodesebagai symlink nodejs. Tidak ada jaminan bahwa saya memiliki semua detail dengan benar.

Keith Thompson
sumber
Jawaban yang sangat menyeluruh memberikan alasan mengapa.
Suraj Jain
1
Solusi untuk masalah nodejsvs nodeadalah memulai file dengan enam baris berikut: 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) */. Ini pertama-tama akan mencoba nodejsdan kemudian mencoba node, dan hanya mencetak pesan kesalahan jika keduanya tidak ditemukan. Penjelasan berada di luar cakupan komentar ini, saya hanya meninggalkannya di sini jika itu membantu siapa pun mengatasi masalah karena jawaban ini memunculkan masalah.
mtraceur
@mtraceur: Saya telah memasukkan komentar Anda ke dalam jawaban saya. Mengapa -pada #!baris?
Keith Thompson
The -dalam #!/bin/sh -hanyalah sebuah kebiasaan yang membuat yakin berperilaku shell tepat di sangat sempit dan tidak mungkin set keadaan yang nama script atau path relatif bahwa shell melihat dimulai dengan -. (Juga, ya, sepertinya setiap distro utama telah berkumpul kembali nodesebagai nama utama. Saya tidak menggali untuk memeriksa ketika membuat komentar saya, tetapi sejauh yang saya tahu hanya pohon keluarga distro Debian yang digunakan nodejs, dan sepertinya seperti mereka semua kembali ke dukungan nodesaat Debian melakukannya.)
mtraceur
Secara teknis, tanda hubung tunggal sebagai argumen pertama tidak berarti "opsi akhir" - ini awalnya berarti "matikan -xdan -v", tetapi sejak awal suka Bourne hanya mengurai argumen pertama sebagai opsi yang memungkinkan, dan karena shell dimulai dengan opsi tersebut dimatikan , menyebabkan shell tidak mencoba mengurai nama skrip sejak aslinya, dan tetap dapat disalahgunakan karena perilaku tersebut dipertahankan di Bourne-like modern untuk alasan kompatibilitas. Jika saya ingat semua sejarah Bourne dan trivia portabilitas saya, benar.
mtraceur
0

Jawaban singkat: Ini adalah jalan menuju penerjemah.

EDIT (Long Answer): Alasan tidak ada garis miring sebelum "node" adalah karena Anda tidak selalu dapat menjamin keandalan #! / Bin /. Bit "/ env" membuat program menjadi lebih lintas platform dengan menjalankan skrip di lingkungan yang dimodifikasi dan lebih andal karena dapat menemukan program interpreter.

Anda belum tentu membutuhkannya, tetapi bagus digunakan untuk memastikan portabilitas (dan profesionalisme)

Kuantum
sumber
1
The /usr/bin/envbit tidak memodifikasi lingkungan. Itu hanya sebuah perintah di lokasi (kebanyakan) yang diketahui yang memanggil perintah lain yang diberikan sebagai argumen, dan mencari $PATHuntuk menemukannya. Intinya adalah bahwa #!baris tersebut membutuhkan jalur lengkap ke perintah yang dipanggil, dan Anda tidak perlu tahu di mana nodediinstal.
Keith Thompson
Itulah yang saya lakukan, terima kasih telah mengklarifikasi!
Quantum