system の挙動

Rubyの組み込み関数であるsystemは、引数の数によって異なる挙動をするようです。今日はそいつではまってしまいました。

例えば、下記のソースファイルから作られた tmp.exe という実行ファイルがあったとします。起動時に与えられたパラメータを出力するだけのプログラムです。

# tmp.exe のソースファイル tmp.cpp
#include <cstdio>
int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; ++i)
    {
        printf("argv[%d] = \"%s\", ", i, argv[i]);
    }
    printf("\n")
    return 0;
}

このtmp.exeを、下記の2種類のRubyスクリプトから呼び出してみます。

# <test_A.rb>
paramA="tmp.exe -f -d"
system(paramA)
# <test_B.rb>
paramB_a="tmp.exe"
paramB_b="-f -d"
system(paramB_a, paramB_b)

一見、行っていることはまったく同じように見えるのですが、出力は次のような結果になります。

$ ./test_A.rb
argv[0] = "tmp.exe", argv[1] = "-f", argv[2] = "-d"
$ ./test_B.rb
argv[1] = "tmp.exe", argv[1] = "-f -d"

びっくりですね。
system関数にパラメータなどをまとめた一つの文字列として渡すと、各パラメータがパースされてtmp.exeに渡されていますが、パラメータを分けて渡すとパースされない状態でtmp.exeに渡されています。
先輩プログラマから教えてもらったのですが、Rubyのsystem関数は、引数が1つのときは、それをシェルを介して実行し、引数が複数のときはプロセス生成のAPIに直接渡すそうです。
伝聞でしかないので、このあたりは使用をしっかり調べておこうと思います。いろいろなことがうろ覚えで、「なんとなく」で書いたコードでも動いてしまうという便利すぎるRubyですが、このあたりの細かいところについても知っておかないと、今日のように思わぬ落とし穴にはまってしまいそうで少し怖いです。今までは「たのしいRuby」だけでやってきたのですが、「プログラミングRuby」あたりも買っておこう。

プログラミングRuby 第2版 言語編

プログラミングRuby 第2版 言語編

プログラミングRuby 第2版 ライブラリ編

プログラミングRuby 第2版 ライブラリ編

たのしいRuby 第2版 Rubyではじめる気軽なプログラミング

たのしいRuby 第2版 Rubyではじめる気軽なプログラミング