web-dev-qa-db-ja.com

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

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

Node.jsができるだけ多くの状況で動作するポータブルスクリプトが必要です。ファイル名がfoo.jsであると仮定すると、スクリプトを2つの方法で実行する必要があります。

  1. ./foo.jsは、nodeまたはnodejs$PATHにある場合にスクリプトを実行します。
  2. node foo.jsもスクリプトを実行します(インタープリターがnodeと呼ばれると仮定)

注:xavierm02と私自身の回答は、ポリグロットスクリプトの2つのバリエーションです。純粋なシバンソリューションが存在する場合、私はまだ関心があります。

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

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

console.log('Hello world!');
_

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

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

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

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

インスピレーションを与えてくれたxavierm02と、追加情報を提供してくれたすべてのコメント者に感謝します。

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

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

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

#!/usr/bin/env node

console.log("Spread the love.");
12
sheldonh
_#!/bin/sh
//bin/false || `which node || which nodejs` << `tail -n +2 $0`
console.log('ok');
_

_//bin/false_は_/bin/false_と同じですが、2番目のスラッシュがノードのコメントに変換する点が異なります。そのため、ここにあります。次に、最初の_||_の右側が評価されます。引用符の代わりに逆引用符を付けた_'which node || which nodejs'_はノードを起動し、_<<_は右側のノードにフィードします。 Dancekのように_//_で始まる区切り文字を使用することもできましたが、うまくいきましたが、最初に2行しかなくて済むので、_tail -n +2 $0_を使用してファイル自体を読み取らせました最初の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');は、スクリプトに置き換える必要があるスクリプトの例です。

10
xavierm02

小さな.shファイルを作成してもかまわない場合は、解決策をいくつかご紹介します。小さなシェルスクリプトを作成して、使用するノードの実行可能ファイルを決定し、Shebangでこのスクリプトを使用できます。

Shebang.sh

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

script.js

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

両方を実行可能としてマークし、./script.jsを実行します。

このようにして、ポリグロットスクリプトを回避します。複数のシバン行を使用することは可能だとは思いませんが、良い考えのようです。

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

3
user27225

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

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

しかし、どちらも選択した回答よりも優れています:

':' //; || echo yes

また、nodeまたはnodejs(両方ではない)のいずれかが見つかることがわかっている場合は、次のように動作します。

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

しかし、それは大きな「もし」であるので、私は選択された答えが最良の答えであり続けると思います。

1
Dave Mason

これで問題が解決しないことは理解していますが、誤った前提で質問されたと確信しています。

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
`-- [email protected]
  `-- [email protected]

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"
1
binki