ユニバーサルNode.jsシバン?


42

Node.jsは最近非常に人気があり、いくつかのスクリプトを書いています。残念ながら、互換性は問題です。公式には、Node.jsインタープリターはと呼ばれることになっていますnodeが、DebianとUbuntuはnodejs代わりに呼び出される実行可能ファイルを出荷しています。

Node.jsが可能な限り多くの状況で使用できるポータブルスクリプトが必要です。ファイル名がfoo.jsであると仮定すると、2つの方法でスクリプトを実行したいです。

  1. ./foo.jsnodeまたはのいずれかでnodejsある場合、スクリプトを実行します$PATH
  2. node foo.jsまた、スクリプトを実行します(インタープリターが呼び出されると仮定node

注: xavierm02と私による回答は、ポリグロットスクリプトの2つのバリエーションです。純粋なシバンソリューションが存在する場合、それにも興味があります。


ビルドシステムによって実行可能ファイルに任意の名前を付けることは許可されているため、これに対する実際の解決策はないと思います。Pythonインタープリターalphacentauriに名前を付けることを妨げるものは何もありません。規則に従ってPythonに名前を付けるだけです。nodeスクリプトに標準名を使用するか、シバンを変更する一種のmakeスクリプトを使用することをお勧めします。

@ G.Kayaalp政治と慣習は別として、多くのDebian / Ubuntu / Fedoraユーザーがいます。私は彼らのために機能するスクリプトを作りたいです。私はこれのためにビルドシステムをセットアップしたくありません(それらを実行する前にシェルスクリプトをビルドしますか?)alphacentauri。という実行可能ファイルがある場合nodejs、Node.jsであることを99%確信できます。nodejsとの両方をサポートしないのはなぜnodeですか?
dancek

nodejs-legacyパッケージをインストールします。これが必要な理由は、名前がgeneric慢すぎるため、他の誰かが最初に名前を取得したためです。ただし、他のパッケージは名前を共有することを望んでいました。
user3710044

回答:


54

私が思いついた最高のものは、ポリグロット(ボーンシェル/ Node.js)スクリプトであるこの「2行シバン」です。

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

console.log('Hello world!');

最初の行は、明らかに、Bourne shell shebangです。Node.jsは、検出したすべてのシバンをバイパスするため、Node.jsに関する限り、これは有効なjavascriptファイルです。

2行目:は、引数を指定してシェルno-op を呼び出し、//実行するnodejsnode、このファイルの名前をパラメーターとして使用します。移植性のcommand -v代わりに使用されwhichます。コマンド置換構文$(...)は厳密にはBourneではないため、1980年代にこれを実行する場合はバックティックを選択してください。

Node.jsは文字列を評価するだけで':'、これはノーオペレーションのようなもので、行の残りの部分はコメントとして解析されます。

ファイルの残りは単なる古いjavascriptです。exec2番目の行が完了すると、サブシェルが終了するため、ファイルの残りの部分はシェルによって読み取られません。

インスピレーションをくれたxavierm02と、追加情報をくれたすべてのコメンターに感謝します!


1
素晴らしい解決策。この':'アプローチの代替手段は、使用することです// 2>/dev/null(これは何をするかですnvm):bashにすると、エラー(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のは、POSIX互換性のある箱のうちのksh AT&Tが付属していません)、これらの日ユビキタスで、マーカス・クーンは推奨しています、それをオフに滞在する(正当な理由で)。私見、あなたはそれを必要としませ。しかし、はい、それはのために十分ですmkshbashdashおよび最近のUnixやGNUシステム内の他のシェル。
ミラビロス

1
素晴らしい解決策。ただし#!/usr/bin/env sh#!/bin/shen.wikipedia.org/wiki/Shebang_%28Unix%29#Portability)の代わりにさらに移植性の高いものを使用します
user7089 14

2
#!/usr/bin/env sh「携帯性の高い」広告には注意が必要です。その同じWikipediaの記事は素晴らしい裏書はないこと「...パス/ usr / binに/ ENVは一般のenvユーティリティのために使用されているので、これは主に働く」と言うと、私の推測では、あなたがいないシステムに実行されますということである/usr/bin/envよりも頻繁に/bin/sh周波数にまったく違いがある場合は、なしでシステムを実行します。
シェルドン

1
@dancekこんにちは、2018年から//bin/sh -c :; exec ...、2行目のよりクリーンなバージョンではないでしょうか?
har-wradim

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

//bin/falseは、/bin/false2番目のスラッシュがノードのコメントに変換することを除いて、同じものです。そのため、ここにあります。次に、最初の右側||が評価されます。'which node || which nodejs'引用符の代わりに逆引用符を使用すると、ノードが起動さ<<れ、右側にあるものがフィードされます。//dancek と同じように区切り文字を使用することもできましtail -n +2 $0たが、機能しますが、最初に2行しかない方がきれいなので、最初の2行を除いてファイル自体を読み取るようにしました。

ノードで実行すると、最初の行はシバンとして認識されて無視され、2行目は1行のコメントになります。

(どうやら、sedを使用して、最初と最後の行なしでテール印刷ファイルの内容を置き換えることができます)


編集前に回答:

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

あなたがしたいことはできないので、代わりにやることはシェルスクリプトを実行することです#!/bin/sh。そのシェルスクリプトは、ノードの実行に必要なファイルのパス、つまりを取得しますwhich node || which nodejs。バッククォートは実行されるようにここにあるので、'which node || which nodejs'(クォートの代わりにバッククォートを使用して)単にノードを呼び出します。次に、を使用してスクリプトをフィードします<<__HERE__あなたのスクリプトの区切り文字です。そして、これconsole.log('ok');はスクリプトで置き換える必要があるスクリプトの例です。


説明はいいでしょう
0xC0000022L

実行前にJavaScriptコードで実行されるパラメーター展開、コマンド置換、および算術展開を回避するために、ヒアドキュメント区切り文字をより適切に引用してください<<'__HERE__'
マナトワーク

これは面白くなってきています!けれどもは//bin/false私MSYS環境では動作しません、と私は私のようバッククォート引用符で囲む必要nodeで存在しますC:\Program Files\...。はい、私は恐ろしい環境で働いています
...-dancek

1
//bin/falseMac OS Xでも動作しません。申し訳ありませんが、これはもう移植性が低いようです。
dancek

2
このwhichコマンドは移植性もありません。
ヨルダン

9

これは、ポリシーが理にかなっているDebianベースのシステムでのみ問題になります。

Fedoraがnodejsと呼ばれるバイナリをいつ提供したかはわかりませんが、見たことはありません。パッケージはnodejsと呼ばれ、nodeと呼ばれるバイナリをインストールします。

シンボリックリンクを使用して、Debianベースのシステムに常識を適用するだけで、正真正銘のシバンを使用できます。他の人はとにかく正気のシバンを使用するので、そのシンボリックリンクが必要になります。

#!/usr/bin/env node

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

すみませんが、これは質問に答えません。私は政治的な観点を理解していますが、それはここでのポイントではありません。
dancek

記録のために、一部のFedoraシステムにはnodejs実行可能ファイルがありますが、それはFedoraのせいではありません。事実を偽ってごめんなさい。
dancek

8
謝罪する必要はありません。その通りです。私の回答はあなたの質問に答えません。それはあなたの質問に語り、その質問は利用可能な解決策よりも有用性の低い解決策に範囲を限定することを示唆しています。歴史に基づいた実践的なアドバイスを提供しています。Nodeは、ここで自分自身を見つける最初のインタプリタではありません。あなたはperl shebangがあるに違いないと宣言するラリーウォールに通じた騒ぎを見たべき#!/usr/bin/perlでした。そうは言っても、あなたは私の提案を気に入ったり適用したりする義務はありません。平和。
シェルドン

3

小さな.shファイルを作成してもかまわないなら、私はあなたのための小さな解決策を持っています。使用する実行可能ノードを決定する小さなシェルスクリプトを作成し、シェバンでこのスクリプトを使用できます。

shebang.sh

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

script.js

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

両方の実行可能ファイルをマークし、実行します./script.js

これにより、ポリグロットスクリプトを回避できます。複数のシバングラインを使用することは考えられませんが、良いアイデアのように思えます。

これで問題は希望通りに解決されますが、誰も気にしないようです。たとえば、uglifyjscoffeescriptはを使用し#!/usr/bin/env nodenpmはシェルスクリプトをエントリポイントとして使用します。シェルスクリプトは再びnameを使用して実行可能ファイルを明示的に呼び出しますnode。私はUbuntuユーザーであり、常にノードをコンパイルするため、これを知りませんでした。これをバグとして報告することを検討しています。


少なくともユーザーにchmod +xshスクリプトを要求する必要があると確信しています。したがって、ノードの実行可能ファイルの場所を指定する変数を設定するようにユーザーに要求することもできます...
xavierm02

@ xavierm02スクリプトchmod +x「d」をリリースできます。そして、私はNODE変数の方がに比べて優れていることに同意しますwhich node || which nodejs。しかし、多くの主要なノードプロジェクトではを使用しているだけ#!/usr/bin/env nodeですが、照会者はすぐに使用できるエクスペリエンスを提供したいと考えています。

1

完全を期すために、2行目を実行する他の方法をいくつか紹介します。

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

しかし、どちらも選択した答えよりも利点はありません。

':' //; || echo yes

また、どちらnodenodejs(または両方ではない)が見つかることがわかっている場合は、次のように機能します。

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

しかし、それは大きな「if」であるため、選択された答えは依然として最良の答えだと思います。


さらに、コマンドでないfoobar // 2>/dev/null限り実行できますfoobar。また、POSIXシステムで見つかっユーティリティの多くは、//引数を指定して実行できます。
dancek

1

これは質問に答えないことを理解していますが、質問は誤った前提で行われていると確信しています。

ここでUbuntuは間違っています。独自のスクリプト用にユニバーサルシェバンを記述しても、のデファクトスタンダードを使用する他のパッケージを変更することはできません#!/usr/bin/env node。nodejsを対象とするスクリプトを実行する場合は、システム提供する必要があります。nodePATH

たとえば、Ubuntuが提供するnpmパッケージでさえ、パッケージのシェバンを書き換えません。

$ 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.