コンテンツへスキップ

コマンドリテラル

コマンドリテラルは、バッククォート` `または`%x`パーセントリテラルで区切られた文字列です。これは、サブシェルで文字列を実行したキャプチャされた出力によってランタイム時に置き換えられます。

通常の文字列と同様に、同じエスケープおよび補間ルールが適用されます。

パーセント文字列リテラルと同様に、`%x`の有効な区切り文字は、丸括弧`()`、角括弧`[]`、波括弧`{}`、山括弧`<>`、およびパイプ`||`です。パイプを除き、すべての区切り文字は入れ子にできます。つまり、文字列内の開始区切り文字は、次の終了区切り文字をエスケープします。

特殊変数`$?`は、コマンドの終了ステータスをProcess::Statusとして保持します。これは、コマンドリテラルと同じスコープ内でのみ使用できます。

`echo foo`  # => "foo"
$?.success? # => true

内部的には、コンパイラはコマンドリテラルを、コマンドを引数として含む文字列リテラルでトップレベルメソッド`()を呼び出すように書き換えます。`echo #{argument}`および`%x(echo #{argument})`は、`("echo #{argument}")`に書き換えられます。

セキュリティに関する懸念

コマンドリテラルは単純なスクリプトのようなツールには役立つ可能性がありますが、ユーザー入力を補間する場合は、コマンドインジェクションに簡単に繋がる可能性があるため、特別な注意が必要です。

user_input = "hello; rm -rf *"
`echo #{user_input}`

このコマンドは`hello`と書き込み、その後に現在の作業ディレクトリ内のすべてのファイルとフォルダーを削除します。

これを避けるために、コマンドリテラルは通常、補間されたユーザー入力で使用すべきではありません。標準ライブラリのProcessは、ユーザー入力をコマンド引数として安全に提供する方法を提供します。

user_input = "hello; rm -rf *"
process = Process.new("echo", [user_input], output: Process::Redirect::Pipe)
process.output.gets_to_end # => "hello; rm -rf *"
process.wait.success?      # => true