Perlのsystem()に@_を渡せなくてハマった。と思ったら単純ミス。
問題
でもユーティリティ関数で呼んでいるsystem()に引数が渡らない!なぜ!><
単純化するとこんなプログラム
#!/usr/bin/env perl use strict; use warnings; use Capture::Tiny qw(capture); sub caller0 { my $out = capture { system('echo', @_) }; print $out; } sub caller1 { my @args = @_; my $out = capture { system('echo', @args) }; print $out; } print "caller 0\n"; caller0('output!'); # 何も出ない print "caller 1\n"; caller1('output!'); # ちゃんと出力される
caller0内で直接@_を使うと動かない。引数に何も渡ってこない。
解決編
ひたすらハマったけど気づいてみると当たり前だった。captureは単なる関数なので、{ ... } は無名サブルーチンを渡しているだけ。なので caller0の引数のつもりで@_と書いても、実際はcaptureに渡しているサブルーチンの引数と解釈される。だからcapture呼ぶ前にコピーしてそっちを渡しましょうっていうだけの話。
おまけ
ちなみに参考にしてるApp::cpanminusのコードはこちら
xt/Run.pm
package xt::Run; use strict; use base qw(Exporter); our @EXPORT = qw(run last_build_log); use Capture::Tiny qw(capture); use File::Temp qw(tempdir); $ENV{PERL_CPANM_HOME} = tempdir(CLEANUP => 1); sub run { my @args = @_; my @notest = $ENV{NOTEST} ? ("--notest") : (); return capture { system($^X, "./script/cpanm.PL", @notest, "--quiet", "--reinstall", @args) }; } sub last_build_log { open my $log, "<", "$ENV{PERL_CPANM_HOME}/latest-build/build.log"; join '', <$log>; } 1;
最初これをコピペしてたのに何で書き換えちゃったんだろう。。。まぁ勉強になりました。