ターミナルコマンドをこよなく愛している、下駄です。
本日も、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"と組み合わせて使うので少しめんどくさい書き方になりますが、確実に文字列をゲットできます。
便利だけど、めんどくさくて、重要な気付きでした・・・
0 件のコメント:
コメントを投稿