4 Nashornとシェル・スクリプト

この項では、JavaScriptスクリプトでUNIXシェル・スクリプト機能を使用できるようにするNashornエンジンの拡張機能について説明します。

注意:

Nashornエンジン、jjsツール、およびjdk.scripting.nashornjdk.scripting.nashorn.shellの各モジュールは、将来のリリースでの削除に備えてJDK 11では非推奨です。

Nashornのシェル・スクリプト拡張機能を有効にするには、-scriptingオプションを指定したjjsコマンドを使用します。たとえば、次のコマンドは対話型モードでシェル・スクリプト拡張機能を有効にした状態でNashornを呼び出します。

jjs -scripting

標準のJavaScriptコメント(//および/* */)に加えて、Nashornは番号記号(#)を使用するシェル形式のコメントもサポートします。スクリプトの最初の文字が番号記号である場合は、-scriptingオプションを指定せずにjjsツールを使用しても、スクリプトを解釈するときにNashornのシェル・スクリプト拡張機能が自動的に有効になります。これは、スクリプトの先頭にshebang (#!)を指定して、スクリプトをシェル実行可能ファイルとして実行する場合に便利です。「Shebang」を参照してください。

Shebang

スクリプト・ファイルの先頭にshebang (#!)を指定すると、そのスクリプト・ファイルをシェル実行可能ファイルとして実行できます。shebangにjjsツールのパスを指定すると、スクリプトを実行したときに、シェルがスクリプトではなくjjsツールを実行し、ツールにスクリプト・ファイルを渡します。

jjsツールは、JAVA_HOME/binディレクトリ(JAVA_HOMEはJDK (またはJRE)のインストール・ディレクトリ)にあります。JDKをインストールすると、通常はJAVA_HOME環境変数が自動的に設定されます。自動的に設定されなかった場合や、複数のバージョンのJDKがインストールされている場合は、手動でJAVA_HOME環境変数をJDKインストール・ディレクトリの正しいパスに設定します。

shebangにjjsツールの直接パスを指定できますが、次のように/usr/binディレクトリにシンボリック・リンクを作成することをお薦めします。

>> cd /usr/bin
>> ln -s $JAVA_HOME/bin/jjs jjs
>> 

注意:

場合によっては、sudoを使用してルート特権でlnコマンドを実行する必要があります。

シンボリック・リンクを設定したら、実行可能ファイルとして実行できるNashornスクリプトを作成できます。また、shebang文にコマンド行オプションを直接追加することもできます。次の例のscriptArgs.jsは、Nashornエンジンのバージョンとスクリプトに渡された引数を出力する実行可能スクリプトを示しています。

#!/usr/bin/jjs -fv
print("Arguments: " + arguments);

scriptArgs.jsファイルをシェル実行可能ファイルとして実行すると、このファイルはNashornによって次のように解釈されます。

>> ./scriptArgs.js -- arg1 arg2 arg3
nashorn full version 1.8.0
Arguments: arg1,arg2,arg3
>>

または、jjsツールのパスがPATH環境変数に含まれている場合は、次のようにしてshebangにjjsを指定できます。

#!/usr/bin/env jjs

文字列補間

文字列補間は、UNIXシェルで変数または式の値を含む文字列を作成するために使用されます。Nashornでシェル・スクリプト機能を有効にすると、同じようにして変数や式を文字列リテラルに埋め込むことができます。

たとえば、次のようにDate()コンストラクタの結果をdate変数に割り当て、ドル記号($)と中カッコ({})を使ってこの変数を文字列に渡すことができます。

jjs> var date = Date()
jjs> "Date and time: ${date}"
Date and time: Mon Aug 19 2013 19:43:08 GMT+0400 (MSK)

前の例は、Date()コンストラクタから返された値がdate変数に割り当てられたときに、日付と時間を表示します。式が評価された時点の日付と時間を表示する場合は、次のようにDate()コンストラクタを直接渡すことができます。

jjs> "Current date and time: ${Date()}"
Current date and time: Mon Aug 19 2013 19:49:53 GMT+0400 (MSK)

文字列補間は、二重引用符で囲まれた文字列でのみ機能します。一重引用符に囲まれた文字列は補間されません。

jjs> 'The variable is ${date}'
The variable is ${date}

ヒア・ドキュメント

ヒア・ドキュメント(heredoc)は、改行とインデントを保持した文字列をUNIXシェルに指定します。Nashornでシェル・スクリプト機能を有効にすると、heredocを含むスクリプトを評価できます。

次の例のscriptHereArgs.jsは、最初に渡された引数を1行目に出力し、2番目の引数をインデントして2行目に出力し、3番目の引数を(空白行の後で)4行目に出力する実行可能スクリプトを示しています。

#!/usr/bin/jjs
print(<<EOD);
${arguments[0]} is normal
    ${arguments[1]} is indented

${arguments[2]} is separated by a blank line
EOD

scriptHereArgs.jsファイルをシェル実行可能ファイルとして実行すると、Nashornエンジンによって次のように解釈されます。

>> ./scriptHereArgs.js -- Line1 Line2 Line3
Line1 is normal
    Line2 is indented

Line3 is separated by a blank line

グローバル・オブジェクト

Nashornでシェル・スクリプト機能を有効にすると、いくつかのグローバル・オブジェクトが定義されます。

$ARG

このグローバル・オブジェクトを使用すると、argumentsオブジェクトを使用する場合と同じようにして、スクリプトに渡された引数にアクセスできます。例:

>> jjs -scripting -- arg1 arg2 arg3
jjs> $ARG
arg1,arg2,arg3
jjs> $ARG[1]
arg2
$ENV

このグローバル・オブジェクトは、現在のすべての環境変数をマップします。例:

jjs> $ENV.USER
johndoe
jjs> $ENV.PWD
/foo/bar
$EXEC()

このグローバル・オブジェクトは、コマンドを実行するためのプロセスを起動します。例:

jjs> $EXEC("ls -l")
total 0
drwxr-xr-x+ 1 johndoe staff 4096 Aug 18 11:03 dir
-rwxrw-r--  1 johndoe staff  168 Aug 19 17:44 file.txt

jjs> 

$EXEC()関数は、2つ目の引数を取ることもできます。この引数は、プロセスの標準入力(stdin)として使用される文字列です。

jjs> $EXEC("cat", "Send this to stdout")
Send this to stdout
jjs> 

注意:

コマンドに入力が不要な場合は、バッククォートによる文字列表記を使ってプロセスを起動できます。たとえば、$EXEC("ls -l")のかわりに`ls -l`を使用できます。

$OPTIONS

このオブジェクトは、nashornの「コマンド・ライン」に渡されるコマンド行オプションを公開します。次に例を示します。

jjs> print("-scripting=" + $OPTIONS_scripting);
jjs> print("--compile-only=" + $OPTIONS_compile_only);
jjs> print("-timezone="+ $OPTIONS_timezone.ID);
$OUT

このグローバル・オブジェクトは、プロセスの最新の標準出力(stdout)を格納するために使用されます。たとえば、$EXEC()の結果は$OUTに保存されます。

$ERR

このグローバル・オブジェクトは、プロセスの最新の標準エラー(stderr)を格納するために使用されます。

$EXIT

このグローバル・オブジェクトは、プロセスの終了コードを格納するために使用されます。終了コードがゼロでない場合、そのプロセスは失敗しています。

その他のNashorn組込み関数

Nashornは、いくつかの組込み関数を定義しています。echoreadLineおよびreadFully関数は、-scriptingモードにのみ定義されます。quitexitloadloadWithNewGlobalObject.bindPropertiesなどの他の拡張機能は、常に利用できます。

quit()
exit()

これらの関数は同義であり、現在のスクリプト・プロセスを終了してシステムに戻ります。システムに返す終了コードを表す整数値を引数として渡すことができます。デフォルトでは、引数を指定しなかった場合の終了コードは、プロセスが正常に終了したことを意味する0に設定されます。

print()
echo()

これらの関数は同義であり、引数として渡された値を文字列に変換し、空白で区切ってstdoutに出力し、続いて改行を出力します。この実装は、java.lang.System.out.print(string)を呼び出した後でjava.lang.System.out.println()を呼び出します。

>> jjs -scripting -- arg1
jjs> var a = "Hello"
jjs> print(123, $ARG[0], a, "World")
123 arg1 Hello World
jjs> 
readLine()

この関数は、stdinの入力を1行だけ読み取り、それをstdoutに送ります。または、結果を変数に割り当てることができます。次の例のように、文字列をreadLine()関数に渡してプロンプト行を作成することもできます。

jjs> var name = readLine("What is your name? ")
What is your name? Bob
jjs> print("Hello, ${name}!")
Hello, Bob!
jjs> 
readFully()

この関数は、文字列引数として渡されたファイルの内容全体を読み取り、それをstdoutに送ります。または、結果を変数に割り当てることができます。

jjs> readFully("text.txt")
This is the contents of the text.txt file located in the current working directory.

jjs> 

readFully()関数は、メソッドbyte[] Files.readAllBytes(Path)を使用してデータを読み取り、返されたバイト配列でバイト・オーダー・マーク(BOM)検出を実行して、読み取ったデータがUnicodeでエンコードされているかどうかを判定します。

load()

この関数は、パス、URLまたはスクリプト・オブジェクトからスクリプトをロードして評価します。

jjs> load("/foo/bar/script.js")
jjs> load("http://example.com/script.js")
jjs> load({name:"script.js", script:"var x = 1 + 1; x;"})
loadWithNewGlobal()

この関数はload()関数とほぼ同じですが、新しいグローバル・オブジェクトを使用してスクリプトが評価されます。これは、スクリプト評価用の新しいコンテキストを作成するための主な手段です。loadWithNewGlobal()に(スクリプトの後で)渡された追加の引数は、新しいコンテキストのargumentsグローバル変数に格納されます。

Object.bindProperties(target, source)

この関数を使用して、sourceオブジェクトのプロパティをオブジェクトtargetにバインドします。この関数を使用することで、グローバル・プロパティの共有が可能になります。たとえば、Document Object Model (DOM)のシミュレーションでは、グローバル・オブジェクトとドキュメントの間でプロパティを共有できます。マルチスレッド・アプリケーションでは、スレッドのグローバル・オブジェクト間で関数を共有できます。

次の例は、objオブジェクトのプロパティをグローバル・オブジェクトにバインドする方法を示しています。

jjs> var obj = {x:34,y:100}
jjs> obj.x
34
jjs> obj.y
100
jjs> x
<shell>:1 ReferenceError: "x" is not defined
jjs> Object.bindProperties(this,obj)
[object global]
jjs> x
34
jjs> y = Math.PI
3.141592653589793
jjs> obj.y
3.141592653589793
jjs> 

関数Object.bindProperties(target, source)は、targetに存在しないsourceのプロパティのみをバインドすることに注意してください。次の例に、関数bindPropertiesobjオブジェクトのプロパティをグローバル・オブジェクトにバインドしようとしています。ただし、xプロパティはすでにグローバル・オブジェクトに存在しています。したがって、関数bindPropertiesは、obj.xをグローバルのxにバインドしません。そのため、obj.xの値を変更しても、グローバルのxの値は変更されません

jjs> x = 2
2
jjs> var obj = {x:3}
jjs> Object.bindProperties(this,obj)
[object global]
jjs> x
2
jjs> x = 4
4
jjs> obj.x
3
jjs> x
4