[PHP] システムコマンドの返り値で失敗しないポイント

2020年5月4日

PHP テクノロジー プログラミング

eyecatch ターミナルコマンドをこよなく愛している、下駄です。

本日も、IT謎掛けやってみます

「ターミナルコマンド」とかけまして・・・ 「独り身で寂しい人の見方」と、ときます。 そのココロは・・・ cat(catコマンドと、猫)を使えば、助かります。

PHPでシステムコマンドを叩く理由

サーバー・サイド言語は、サーバー内の操作をほぼなんでもできるようになっていますが、それでも、システムコマンドを実行した方が効率がいい場合が多々あります。 ただし、同じシステムで常に同じサーバーOSを使用する場合は問題ないのですが、開発段階は、ウィンドウズOSで、公開サーバーはLINUXという場合には、入力するコマンドが違ってくるので、システムコマンドを利用する場合は、そうした環境差分を注意しなければいけません。 もっと細かく言うと、システムコマンドそれぞれにも、バージョンが存在していて、そのバージョンが違う事で、結果が若干変わってきたり、上位互換や下位互換が担保されているとは限らないので、エラーが返ってしまうケースも稀にあります。 そうした事を十分理解している場合、PHPでシステムコマンドを利用する価値は大いにあります。 具体的に言うと、特定のデータフォルダ内に入っている画像ファイル(jpegファイルのみ)の一覧を取得したい場合、PHPで行うと・・・ $lists = array_diff(scandir(%検索するdirectory) , array(".","..")); foreach($lists as $filename){ if(preg_match("/\.jpg/",$filename)){ echo $file .PHP_EOL; } } 通常、jpg拡張子だけを判別したい場合は、上記のように書きますが(もっと効率的に書く方法もありますが・・・)、 システムコマンドを利用すると、以下のような感じです。 exec("ls %検索したいdirectory/*.jpg" , $responce); print_r($responce); lsコマンドって便利なんですね・・・ もちろん、PHPで関数を作ってプログラム本文を短くすることは可能ですが、決定的な違いは、そのスピードです。 phpのloop文(for文やforeach文やwhile文)のスピードとシステムコマンドのスピードは格段に違います。 数十件程度であれば、体感的にあまりかわらないのですが、1000個、1万個・・・となってくると、phpだけの処理では、遅くて仕方がない状態になります。 IOに直結することができる、システムコマンドって、クオリティ担保にも十分に役立てるんですね。

execを使っていて起きたトラブル

そんな便利なPHPのシステムコマンド連携ですが、ごく稀に、想定した返り値が得られない時があります。 というか、そんな事があって、プログラムバグにつながった事を経験したので、その話をしたいと思います。 それは、catコマンドなどを使って、テキスト内部の行の文字列を取得しようと思った時に、行の末尾に半角スペースが入っていると、execコマンドを通ると、勝手にtrim処理が掛かっていたという事件です。 サンプルプログラムを実行してみるとよくわかります。 1,a,"A" 2,b,"B " 3,c,"c" 4,d, 5,e, " e " ぱっと見で分かりづらいですが、4行目の末尾には、半角スペースが入っています。 "4,d,____"(半角スペースを_アンダースコアに変換してみました) <?php exec("cat sample.txt" , $res); print_r($res); # Responce Array ( [0] => 1,a,"A"┘ [1] => 2,b,"B "┘ [2] => 3,c,"c"┘ [3] => 4,d,┘ [4] => 5,e, " e "┘ ) この状態で、見た目にはわかりませんが、行末の半角スペースが無くなっています。 ※改行コードを"┘"に置き換えています。 もちろん、サーバー側で、実際にコマンドを叩いてみると以下の通りです。 $ cat sample.txt 1,a,"A"┘ 2,b,"B "┘ 3,c,"c"┘ 4,d,____┘ __5,e, " e "┘ 5行目の先頭文字列にある半角スペースは残ったままになっているので、単純にtrimが実行されているのではなく、行末のみtrimされているようです。 ここからは、僕の推測ですが、execコマンドは、改行コマンドで分割されて、配列で受け取れるようになる特徴があるので、 改行コマンドを、¥nと¥rだけでなく、半角スペースで終わる行末も対象になっていると考えられます。 通常であれば、行末の半角スペースなどは、ファイル名でもあり得ないし、単語取得をしたい場合は、むしろ、trimしてくれて有り難いぐらいなのだが、文字列をきっちり取得したい場合は、この状態ではエラーになってしまいます。

passthruを使うと解決できる

そこで、phpには、execの他に、system関数や、shell_exec関数・・・などなど、たくさんのシステム関数連携機能を持っているのですが、 参考 : PHPはコマンド実行関数多すぎだろ https://pasela.hatenablog.com/entry/20081217/exec その中の"passthru"を使うことで解決できました。 書き方が少しめんどくさくなりますが、 <?php ob_start(); passthru("cat sample.txt"); $responce = ob_get_contents(); ob_end_clean(); print_r($responce); # Responce 1,a,"A"┘ 2,b,"B "┘ 3,c,"c"┘ 4,d,____┘ ____5,e, " e "┘ "ob_start"や"ob_get_contents"と組み合わせて使うので少しめんどくさい書き方になりますが、確実に文字列をゲットできます。 便利だけど、めんどくさくて、重要な気付きでした・・・

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ