Namazu-devel-ja(旧)


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Do NOT use system()



At Wed, 31 Jul 2002 19:31:52 +0900 (JST),
Hajime BABA wrote:
> Perlクックブックを見ていて気が付きましたが、Perl の system() は、
>   system("/bin/ls *");
> と
>   system("/bin/ls", "*");
> のようにリスト形式で起動するのとでは、シェル(/bin/sh -c)を呼ぶか否
> かでえらい違うということに、いまごろ気が付きました。ということは、

perldoc -f system にも書いてありますよ。

> 標準(エラー)出力をリダイレクトする必要のない外部コマンド起動には、
> system(LIST) 形式なら問題なくなるとおもいますがどうなのでしょうか。

はい、問題ないです。引数が 1つの場合でも、シェルのメタキャラクタ
(っぽい?)文字のが入っていなければ /bin/sh は起動されません。
どの文字がそのように判断されるかは、Perl のソースを読まないと
わかりません。

> たとえば、msword.pl の
>   system("$wordconvpath $options $tmpfile $ofile");
> は、$options を @options となるようにして、
>   system($wordconvpath, @options, $tmpfile, $ofile);
> と変えるということです。pdftotext を起動するところも同様です。
> これは簡単なので、ぼくの解釈が間違ってなければすぐにやってしまって
> いいんじゃないかな。

間違っていません。

> (コードが煩雑になりますが)地道に fork()/exec() を使うように直すか、
> 少しでも見易くということなら、stderr が不要なら $fh->open("-|"); 
> とするか、stderr が必要なら IPC::Open3 ということになるのでしょうか。
> うーん、どれが良いのか判別がつけられません...

UNIX (というか /bin/sh) だけがターゲットなら

    $ENV{'A'} = 'ls';
    $ENV{'B'} = '-l';
    $ENV{'C'} = '.';
    $ENV{'D'} = 'ls -l.out';
    system('"$A" "$B" "$C" >"$D"');

なんてのもできるんだが…。;-p

> あと、同じことですが、
>     my $fh_cmd = util::efopen("$wvversionpath $tmpfile |");
> もシェルを起動しているのですよね?これも直すとなると結構たいへんに

これも、open, IO::File でも、シェルが起動されない場合もありますが、
そのような書き方は避けるべきですね。

-- 
SATOH Fumiyasu - fumiya @net-thrust.com, @samba.gr.jp, @namazu.org or ...
THRUST Co., Ltd. @ Fujisawa, Kanagawa, Japan - http://www.net-thrust.com
Samba-JP, aka `Samba Users Group Japan'      - http://www.samba.gr.jp
Apache-JP(?), aka `Japan Apache Users Group' - http://www.apache.jp
Namazu, a full-text search engine            - http://www.namazu.org

-- cgi-ml より引用
Date: Wed, 5 Sep 2001 15:35:48 +0900
From: Taro Takemura <taro@xxxx>
Reply-To: cgi-lst@xxxxxx (cgi)
Subject: [cgi:40253] Re: sendmail を外部ファイルで
To: cgi-lst@xxxxxx (cgi ML)
Message-Id: <20010905.153230.74740159.taro@xxxxxxxxxxxxxxxxxxxxxxx>
In-Reply-To: <877kvf88on.wl@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
References: <20010904125253.9AB3.IWASAKI@xxxxxxxxxxxx>
	<20010904130921.BCBB.IWANAMI@xxxxxxxxx>
	<877kvf88on.wl@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
Mime-Version: 1.0
Content-Type: Text/Plain; charset=iso-2022-jp
Content-Transfer-Encoding: 7bit

竹村です。

> さとうふみやすです。
> Perl 5.005_03 までは、 パイプ付きの open() では、起動しようとしたコマンド
> がなくても undef を返さないんですが、Perl 5.6.1 では undef を返すん
> ですね。
> $ perl -e 'print "$]\n";'
> 5.006001
> $ perl -e 'open(A, "|cat") || die "cannot open command: $!\n";'
> $ perl -e 'open(A, "|dog") || die "cannot open command: $!\n";'
> cannot open command: そのようなファイルやディレクトリはありません
> $ perl -e 'print "$]\n";'
> 5.00503
> $ perl -e 'open(A, "|cat") || die "cannot open command: $!\n";'
> $ perl -e 'open(A, "|dog") || die "cannot open command: $!\n";'
> たぶん open() で /bin/sh が利用される条件が変ったのですね。

Perl 5.005_03 と Perl 5.6.0 で確認してみたところ、
どちらも /bin/sh は利用していないようでした。
strace(1) で追いかけてみたところ、Perl 5.005_03 では pipe(2) してから
fork(2) 後に、子プロセス側で環境変数PATHに従って順次 execve(2) を
試みたあと _exit(2) するだけですが、Perl 5.6.0 では fork(2) 前に
pipe(2) を余分に呼びだして親子間の制御用パイプを作成し、
子プロセス側は execve(2) が全て失敗するとこのパイプを通じて
親プロセス側にコマンドの実行に失敗したことを伝えていました。
open の引数(先頭や末尾の'|'を除いて)にシェルのメタ文字が含まれる時は
/bin/sh -c 経由で実行されましたが、上記の例では /bin/sh を介さずに
直接実行を試みていました。

Perl 5.005_03 などでは上記の通り open は起動しようとしたコマンドが
存在しないことを検知できませんが、SIGPIPE をトラップすることまたは
SIGPIPE を無視する($SIG{'PIPE'}='IGNORE')ようにして
print や close の返り値をチェックすることによって
パイプが正常に働いていないことを検出するのは可能です。

> $ perl -e 'open(A, "|cat|dog") || die "cannot open command: $!\n";'
> sh: dog: command not found
> Perl で、シェルを絶対に介さずにパイプが使える open() (IO::File) 相当の
> 実装が欲しいのですが、なにかよいものないでしょうか。

IPC::Open2 では駄目でしょうか?
"without using the shell" な書式として
$pid = open2(\*RDRFH, \*WTRFH, 'some', 'cmd', 'and', 'args');
というのがあるようです。
 _____________________________________________________________________
///_/_/_/   CGI mailing list : http://forest.ne.jp/cgi-ml/   _/_/_////