ノードファイルの先頭で「/ usr / bin / env node」は正確に何をしますか?


109

#!/usr/bin/env nodeのいくつかの例の冒頭でこの行を見たnodejsことがありますが、その行の理由に答えられるトピックを見つけることなくググっていました。

単語の性質上、検索はそれほど簡単ではありません。

私は最近、いくつかの本を読んだことがjavascriptありnodejs、それらのいずれかでそれを目にしたことを覚えていません。

例が必要な場合は、RabbitMQ公式チュートリアルを見ることができます。彼らはほとんどすべての例にそれを持っています。ここにその1つを示します。

#!/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 2015年

@Gepser:わかった。つまりnpm、Node.jsソーススクリプトを(潜在的にグローバルに利用可能な)CLIとしてインストールする場合、シバン行を使用する必要ありnpm、Windowsでも機能します。もう一度更新された私の答えを参照してください。
mklement0 2015年

「単語の性質上、検索はそれほど簡単ではありません」 – この特定の検索ユースケースでduckduckgo.comを試すことをお勧めします
Ricardo

回答:


146

#!/usr/bin/env nodeあるのインスタンスシェバング行は上の実行可能なプレーンテキストファイル内の最初の行Unixライクなプラットフォームの実行のためにそのファイルを渡すためにどのような通訳システムを告げる魔法次のコマンドラインを経由して、#!接頭辞(と呼ばれるシェバング) 。

注:Windowsはシバン線をサポートしていないため、実際には無視されます。Windowsでは、特定のファイルのファイル名拡張子のみが、どの実行可能ファイルがそれを解釈するかを決定します。ただし、のコンテキストではまだ必要ですnpm[1]

以下のシバン行の一般的な説明は、Unixのようなプラットフォームに限定されています。

次の説明では、Node.jsで実行するためのソースコードを含むファイルの名前を単にと仮定しますfile

  • あなたは、この行が必要あなたはNode.jsのソースファイルを起動したい場合は、直接それ自体で実行可能ファイルとして、 -これは、ファイルは次のようなコマンドで実行可能としてマークされていることを前提としchmod +x ./file、その後、ファイルを起動することを可能にします、たとえば、、./fileまたは、$PATH変数にリストされているディレクトリのいずれかにある場合は、単にとしてfile

    • 具体的には、npm パッケージの一部としてNode.jsソースファイルに基づいてCLIを作成し、パッケージのファイルキーの値に基づいてCLIをインストールするためのシバン行が必要ですグローバルにインストールされたパッケージでどのように機能するかについては、この回答も参照してください。脚注[1]は、これがWindowsでどのように処理されるかを示しています。npm"bin"package.json
  • あなたは必要ありません経由で明示的にファイルを呼び出すために、この行をnode、例えば、インタプリタnode ./file


オプションの背景情報

#!/usr/bin/env <executableName>インタプリタを移植可能に指定する方法です。一言で言えば、変数に<executableName>リストされているディレクトリの中から(最初に)見つけた場所で実行し$PATHます(そして、手元のファイルへのパスを暗黙的に渡します)。

これは、特定のインタープリターがプラットフォーム間でさまざまな場所にインストールされる可能性があるという事実を説明しています。これはnode、Node.jsバイナリの場合は間違いありません。

対照的に、の位置envユーティリティ自体はであることに依拠することができる同じつまり、プラットフォーム間での位置/usr/bin/env-及び指定完全実行可能ファイルへのパスがされ必要シェバングラインに。

POSIXユーティリティenvは、ファイル名で検索し、で実行可能ファイルを実行するために、ここで再利用されていることに注意してください$PATH
の真の目的はenv、コマンドの環境を管理することですenv。POSIX仕様キーストンプソンの役立つ回答を参照してください。


有効なJavaScriptコードではない(POSIXのようなシェルや他のインタープリターとは異なり、JavaScriptのコメント文字ではない)ことを考えると、Node.jsがシバン行の構文例外を作成していることにも注意してください#


[1]クロスプラットフォームの一貫性のために、(プロパティを介して)パッケージのファイルで指定された実行可能ファイルをインストールするときに、Windowsラッパーファイル(バッチファイル)をnpm作成します *.cmd。基本的に、これらのラッパーバッチファイル Unixシバン機能を模倣しています。シバン行で指定された実行可能ファイルを使用してターゲットファイルを明示的呼び出します。したがって、Windowsでのみ実行するつもりでも、スクリプトにシバン行を含める必要があります - この回答を参照してください詳細は私のもの。 以来ファイルがなしで起動することができますpackage.json"bin"
*.cmd.cmdこれにより、シームレスなクロスプラットフォームエクスペリエンスが実現します。WindowsとUnixの両方で、インストールnpmされているCLIを、元の拡張子のない名前で効果的に呼び出すことができます。


私のようなダミーの説明や概要を教えてください。
Andrew Lam

4
@AndrewLam:上のWindowsのようなファイル名の拡張子.cmd.py、そのようなファイルを実行するために使用されるかのプログラムを決定します。Unixでは、shebang行がその機能を実行します。npmサポートされているすべてのプラットフォームで機能させるには、Windowsでもシバンラインが必要です。
mklement0 2017年

28

インタープリターによって実行されるスクリプトは、通常、最上部にシバン行があり、OSに実行方法を指示します。

という名前のスクリプトがfooあり、その最初の行がである#!/bin/sh場合、システムはその最初の行を読み取り、に相当するものを実行し/bin/sh fooます。このため、ほとんどのインタープリターは、スクリプトファイルの名前をコマンドライン引数として受け入れるように設定されています。

に続くインタープリター名#!は絶対パスでなければなりません。OSはあなた$PATHを検索してインタープリターを見つけません。

で実行するスクリプトがある場合node、最初の行を書く明白な方法は次のとおりです。

#!/usr/bin/node

ただし、nodeコマンドがにインストールされていない場合は機能しません/usr/bin

一般的な回避策は、env次のコマンドを使用することです(これは、この目的を意図したものではありませ)。

#!/usr/bin/env node

スクリプトがと呼ばれるfoo場合、OSは同等のことを行います

/usr/bin/env node foo

envコマンドは、名前がそのコマンドに任意の次の引数を渡し、そのコマンドラインで指定された別のコマンドを実行します。ここで使用する理由は、コマンドenvを検索する$PATHためです。もしそうnodeでインストールされている/usr/local/bin/node、とあなたが持っている/usr/local/binあなたに$PATHenvコマンドが起動します/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ます。私のメモによると、それはUbuntu 12.04と12.10の間でに変更され/usr/bin/nodeました/usr/bin/nodejs#!/usr/bin/envそれとトリックしませんヘルプ(あなたは、シンボリックリンクまたは類似のものを設定していない限り)。

更新:mtraceurによるコメントは(再フォーマット済み)と述べています。

nodejsとnodeの問題の回避策は、次の6行でファイルを開始することです。

#!/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を使用していません。私が最初にこの回答を投稿してから数年でnodejsvs.のnode問題が解決されたことを願っています。Ubuntu 18.04では、nodejsパッケージは/usr/bin/nodejsへのシンボリックリンクとしてインストールされ/usr/bin/nodeます。以前の一部のOS(UbuntuまたはLinux Mint、どちらかはわかりません)では、へのシンボリックリンクとしてnodejs-legacy提供さnodeれるパッケージがありましたnodejs。すべての詳細が正しいことを保証するものではありません。


物事の理由を提供する非常に徹底的な答え。
Suraj Jain

1
nodejsvs node問題の回避策は、次の6行でファイルを開始することです: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)*/。これは最初に試行nodejsしてから試行しnode、両方が見つからない場合にのみエラーメッセージを出力します。説明はこれらのコメントの範囲外です。この回答が問題を提起したので誰かが問題に対処するのに役立つ場合に備えて、ここではそのままにしておきます。
mtraceur

@mtraceur:私はあなたのコメントを私の答えに組み込んだ。なぜ-上の#!ライン?
キース・トンプソン、

-中には、#!/bin/sh -右のスクリプト名または相対パスは、シェルで始まるを見ているという状況の極端に狭いとそうセットで必ずシェルの振る舞いを作るだけの習慣です-。(また、はい、それはすべての主流のディストリビューションはに収束戻っていたように見えるnode主名などを。私は私のコメントを作成する際にチェックするために掘って行きませんでしたが、私の知る限り唯一のDebianディストリビューションファミリーツリーが使用さ知っているとしてnodejs、それが見えますnodeDebianがサポートするようになったとき、彼らもまたすべてサポートに戻ったように。)
mtraceur

技術的には、最初の引数としての単一のダッシュは「オプションの終わり」を意味しませんでした-それはもともと「オフ-xおよび-v」を意味しましたが、初期のBourne-likesは可能なオプションとして最初の引数のみを解析し、シェルはそれらのオプションをオフにして開始するため、シェルが元のスクリプト名を解析しないようにすることは悪用可能であり、互換性の理由で動作が現代のBourneのように維持されているため、悪用可能のままです。私のボーンの歴史と携帯性の雑学をすべて覚えていれば。
mtraceur

0

短い答え:それは通訳への道です。

編集(長回答):「ノード」の前にスラッシュがないのは、常に#!/ bin /の信頼性を保証できないためです。「/ env」ビットは、変更された環境でスクリプトを実行することにより、プログラムをよりクロスプラットフォームにし、インタープリタープログラムをより確実に見つけることができるようにします。

必ずしも必要ではありませんが、移植性(および専門性)を確保するために使用することをお勧めします


1
/usr/bin/envビットは、環境を変更しません。引数として指定された別のコマンドを呼び出し、検索$PATHして見つける、(ほとんどの)既知の場所にあるコマンドです。ポイントは、#!行が呼び出されるコマンドへのフルパスを必要とし、どこnodeにインストールされているかは必ずしもわからないということです。
キース・トンプソン、

それが私が説明していたことでした、明確にしてくれてありがとう!
Quantum
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.