性格いい人が優勝

するようなコマンドをPHPから実行しています。

$command = "unzip -c -qq {圧縮ファイル} 1> {出力ファイル} 2> {エラーファイル}";
$ret = exec($command, $output, $return_var);

おおむね問題ないのですが、disk full のエラーを起こした場合だけは、それをエラー
として検出することができないという問題にぶつかりました。

以下、上記のプログラムを実行して、解凍した結果がdisk full状態に陥った場合に
返してきた値や出力ファイルの結果です。

$ret: 空文字
$output:空の配列
$return_var:0
出力先ファイル:0バイトのファイルが生成される。
エラー出力ファイル:0バイトのファイルが生成される。
error_log:出力されない

出力ファイルが、ゼロバイトなので、それをもってエラーとすることも
検討しましたが、具体的なエラー内容がわからないままでは、具体的な
エラーハンドリングができないので、それでは少々困ります。

標準出力にしている理由は、解凍するファイル名を、任意に指定した形で
解凍させたいのですが、unzipコマンドでは、解凍するファイル名を
解凍時に任意に変更して出力することができないため、標準出力でそれを
実現しようとしています。

解凍先に任意のディレクトリ名を指定して、解凍することも検討していますが、
できることなら、現在のシステムの都合上、ディレクトリを作成することは
避けたいという事情もあります。


標準出力を使用した方法で、zipファイル解凍時のdisk fullエラーを検出する方法を、
どなたかご教授いただけませんでしょうか。

あるいは、unzipではなくても、
「zip書庫内のファイルを任意のファイル名で解凍」し、
「disk fullエラーを含む、各エラーが検知できる」
という条件を満たしていれば、そちらも、お教えいただけますと助かります。

また、システムの仕様として、
「1つの圧縮ファイルには、1つのファイルしか格納しない」
「書庫内にディレクトリ情報は保存しない」
というルールを適用しています。解決の可能性が高まるのでしたら、
このルール上限定でもかまいません。

環境は、以下の通りです。
CentOS4/5
PHP4系/5系
Apache2.0

何卒、よろしくお願いいたします。

A 回答 (4件)

ああああああああああ


そうですね、ディスクフルを書き込んで検出できるわけが無いですね。
なんかパラドックス的な事になってますね・・・

方法は無くもないですが、、
unzip-5.5.2:
---
fileio.c(1154): /* Function disk_error() */
fileio.c(1174): } /* end function disk_error() */
---
にディスク依存しない独自のエラールーチン仕込むとか。。。

しかし、やはり標準出力でディスクに書き込んでいる点で、詰んでるように思えます。

ですので、
---
ロックファイルを作ってロック
ユニークなテンポラリディレクトリを作成
書庫ファイルを作製したディレクトリへ移動
proc_open()等で、unzipコマンドで普通に解凍
標準出力を確認してメッセージ確認
proc_close()等で戻り値確認
解凍されたファイルを任意のファイル名にリネームして元の位置に移動
テンポラリディレクトリを削除
ロックファイルのロックを解除
---
とかそういうカンジになるのではないですかね~


あとは出力先をディスクフル検出可能なデバイスにするとか。


もう1つ、根本的な解決にはならないとは思いますが、
zipファイルの仕様として、ファイルサイズのエリアが4バイト=32ビット、
zipファイルに格納できる最大ファイルサイズは約4GB(4294967296 bytes)なので、
解凍先ディスクが4GB+α(例えば適当に10GBくらい)を下回っていたら、
それをもってディスクフルとみなす、という策があります。
1TBとか普通の時代なので10GBくらいはいけるんじゃないでしょうか


---
潜在的にディスクフル問題を抱えているアプリは結構あるので、
本当にディスク容量が無くなってしまうと他のアプリでも挙動がおかしくなりそうです
    • good
    • 0
この回答へのお礼

詳細なご回答、ありがとうございます。

1つ目のアドバイスについては、残念ながらCの知識に疎いため、よく理解ができませんでした。ただ、こういう独自のカスタマイズということが可能なんですね。参考になりました。

結論としては、仰るとおり、テンポラリディレクトリを作成する方法になりそうです。システムの仕様の都合上、できるだけこの方法は回避したいところでしたが、機能の安全性を確保するという命題が最優先ですので、標準出力でのエラー検出はあきらめようと思います。目的は、解凍先のファイル名にユニークなファイル名を使うことですので、それをディレクトリにやらせようと思います。

出力先を変更する方法については、すでに稼働中のシステムであることと、規模がそこそこ大きいためになかなか小回りや融通が効かず、使える策がかなり限られてしまうことなどで、なかなか難しいところです。

解凍ファイルサイズや使用率のチェックは、解凍処理直前に仕込みました。やはり、DISK FULLの発生自体を、できるかぎり回避すべきだと思いますので。


いろいろと、参考になるアドバイスをありがとうございました。おかげさまで、今回の件では、非常に勉強になりました。

お礼日時:2010/12/08 12:10

># unzip -c hoge.zip 1> fuga.txt


>
>と、標準出力オプションを与えて実行し、disk fullを発生させた場合は、何もエラーは出力されませんで
>した。これは、お教えいただいた方法でも、おそらく検知できないことを示すのではないかと思いました。

エラーは通常、標準エラー出力に出力されます。
参考URLに詳しく載ってますので見てみてください。
抜粋:
---
●sh・bash の場合
まず、
標準出力は 1 番
標準エラー出力は 2 番
ということを覚えてほしい (ちなみに標準入力は 0 番)。
---

私は「>&」でまとめちゃいますけど(理由なし)w

# unzip -c hoge.zip 2> fuga.txt

で試してみてください。

参考URL:http://x68000.q-e-d.net/~68user/unix/pickup?%A5% …
    • good
    • 0
この回答へのお礼

早速のご回答ありがとうございます。

失礼しました、コマンドの例の表記に、標準エラー出力を記載し忘れておりました。試したコマンドは、正しくは以下の通りです。

# unzip -c hoge.zip 1> fuga.txt 2> piyo.txt

この状態で、disk fullが発生した場合の結果としては、

 fuga.txt: 途中で途切れたデータが出力される
 piyo.txt: 何も生成されない
 実行直後の # echo $? の戻り値は ゼロ を返す

となりました。おそらく、エラー出力する時点では、disk full状態のために出力される領域がないので、生成できないのだろうと考えております。

また、補足になりますが、PHP上からexecコマンド等で実行した場合は、

$command = "unzip -c -qq hoge.zip 1> fuga.txt 2> piyo.txt";
$ret = exec($command, $output, $return_var);

 $ret: 空文字
 $output:空の配列
 $return_var:0
 出力先ファイル:ゼロバイトのファイルが生成される
 エラー出力ファイル:ゼロバイトのファイルが生成される

と、少々異なった結果となりました。PHP側である程度の不正なファイルハンドリングを監視しているのだろうと思いますが、エラー出力ファイルもゼロバイトという結果をみて、unzipコマンドが標準出力時に発生するエラーを関知していないのではないかと考えました。

標準出力をしている時点で、unzipがファイル書き込みのエラー検出の責任を放棄しているのは、何となくわかる気もしますが、別のプロセスでもかまわないので、システムとして何かしらのログなり、エラーを示す情報をはき出してくれていることを期待していたのですが、どうも見つけ出すことができませんでした。

お礼日時:2010/12/07 10:54

unzipでdisk fullになった場合、unzipあるいはunzip -vとかでエラーメッセージでますよね?


それを、
---
CXV. 出力制御関数(output control)
---
を用いてstdout/stderrへの出力を取得し、disk fullメッセージを確認すればよいのではないでしょうか?
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

今回の質問のポイントの1つとして、できれば「unzipに標準出力オプション(-c)を使用した状態で、disk fullを検知したい」という、仕様上の縛りみたいなものがありました。これは、圧縮されている書庫内のファイルを、任意のファイル名に指定しながら展開したいという理由があったためです。(unzipコマンドには、そのような機能は見あたりませんでした)

この標準出力オプションを使わなければ、仰るとおり実現可能だと思うのですが、たとえば直接コマンドライン上で、

# unzip hoge.zip

と実行した際に、disk fullを起こした場合は、画面上にもそのエラー状況が表示されたのですが、これを、

# unzip -c hoge.zip 1> fuga.txt

と、標準出力オプションを与えて実行し、disk fullを発生させた場合は、何もエラーは出力されませんでした。これは、お教えいただいた方法でも、おそらく検知できないことを示すのではないかと思いました。
※直接実行して確かめることができていないので、推測になります。取り急ぎの回答で申し訳ありません。


しかし、いただいた方法は、エラーメッセージの取得に役立ちそうですので、有効利用させていただきたいと思います。おそらくこのままですと、標準出力を利用する方法はNGとなりそうですので(任意の名前で一時ディレクトリを掘ってそこに展開する方法に切り替えます)、その際に、教えていただいた関数を利用させていただきます。

お礼日時:2010/12/06 10:59

-Z オプションを付けるか zipinfo を使って、先にファイルのサイズを調べてはいかがでしょうか。



同じことですが、Zip 拡張を利用してもサイズを調べることが出来ます。
http://www.php.net/manual/ja/book.zip.php
http://www.php.net/manual/ja/zip.examples.php
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

すみません、少々慌てて質問を投稿してしまったため、提供する
情報が足りていませんでした。

おっしゃるとおり、展開後のサイズを事前に確認することも
考えてはいたのですが、複数ユーザが同時にアクセスしたり、
他の処理が平行して走っていることを考慮すると、DISKの残容量が
事前の確認どおりにならない可能性が残ってしまいます。

とはいえ、このような間接的な回避方法(事前チェックなど)は
必須と考えており、実際に実装しておりました。

しかし、やはり何かの拍子にDISK FULLを起こした場合、直接的に
そのエラー状態を検知できるのが、もっとも望ましいかなと考えて
おりました。

また、補足ですが、一時ディレクトリを用意して展開することを
検討していると書きましたが、標準出力(-cオプション)を使用しな
ければ、DISK FULLのエラーを、unzipコマンド自体が返してくれる
という理由からでした。


その後、いろいろと調べてみましたが、どうやら、unzipコマンド
に限らず、標準出力>ファイル保存によって、DISK FULLを起こした
場合のエラー検知は難しいようですね。。。

お礼日時:2010/12/02 16:08

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!