Symfony6で突然zipコマンドが動かなくなった問題


Symfony3.4では動いてたんや

Symfony\Processを用いて、複数ファイルをワイルドカード(*)を使用して
1つのZIPファイルにまとめるコマンドを実行していました。

Processクラスに渡したコマンドは以下の通りです

new Process(['zip', '-jrq', '-P', 'password', 'path/to/test.zip', 'path/to/test*.csv']);
# コマンド実行前
path
└── to
    ├── test_1.csv
    ├── test_2.csv
    └── test_3.csv

# コマンド実行後
path
└── to
    ├── test_1.csv
    ├── test_2.csv
    ├── test_3.csv
    └── test.zip # test_1.csv, test_2.csv, test_3.csvが全て入っている

このコマンドをSymfony3.4で実行すると、問題なく動作します。

なぜかSymfony6では動かない...

しかし、Symfony6でそのコマンドを走らせると下記のようなエラーが出るようになりました。
もちろんZIPファイルも作成されていません。

zip error: Notiong to do ! (try: zip -jrq -P password /path/to/test.zip . -i  /path/to/test*.csv)

zip -jrq -P password /path/to/test.zip . -i /path/to/test*.csv
代わりに試してくれよなと言われてしまいました。

原因

Symfonyのgithub discussionにて、このエラーの原因について教えていただきました。

エラーの原因は、Processクラスのコンストラクタに渡されたコマンドの取り扱い方法が
Symfony3.4と6で変わっていたことでした。
その変更に伴い、圧縮対象のファイル名に含まれているワイルドカード(*)の解釈の仕方も変わりました。

  • Symfony3.4

    • コンストラクタに渡されたコマンドは一度シェルでプリプロセッシングされてから実行される
    • ワイルドカード(*)は、globとして扱われる
    • test_1.csv test_2.csv test_3.csvが圧縮対象のファイルになる
  • Symfony6

    • コンストラクタに渡されたコマンドはシェルでラップされることなく直接実行される
    • ワイルドカード(*)は、そのまま文字列として扱われる
    • test*.csvというファイル名は存在しないのでエラーが出る

解決策

解決策は、エラーメッセージが既に教えてくれていました。

(try: zip -jrq -P password /path/to/test.zip . -i  /path/to/test*.csv)

これをそのままProcessクラスのコンストラクタに渡せば無事実行することができました。
-iオプションを用いることで、ワイルドカード(*)をglobとして認識させることができるようです。

new Process(['zip', '-jrq', '-P', 'password', 'path/to/test.zip', '.', '-i' ,'path/to/test*.csv']);

後書き

普段はLaravelばかり触っており、Symfonyやシェルについてはまだまだ勉強中です。
もし、間違っているところなどありましたらコメント欄にて教えていただけると幸いです。

シェルのプリプロセッシングって厳密には何しているんでしょうか・・・?
詳しい方教えてください。

最後まで読んでいただきありがとうございました。