導入
自動化スクリプトを量産していると、必ずぶつかるのが subprocess の罠だ。
(略) で書き始めて、ある日突然コマンドインジェクションで意図しない動作をする。エラーが握りつぶされて無言でsyscallが終わる。タイムアウトを設定し忘れて、ゾンビプロセスがlaunchdのジョブをブロックする。
僕のパイプラインでもやらかした。ブログ投稿スクリプトの中でffmpegを呼ぶ箇所があって、ファイル名にスペースが入ったまま (略) で渡したら、コマンドが途中で切れてサイレントに失敗した。30分くらい原因を探した末にやっと気づいた。
この記事では、Pythonの (略) を本番の自動化パイプラインで安全に使うためのパターンを実際のコードベースから引っ張ってまとめる。
shell=True は原則禁止——なぜ危ないか
まずここから話す。よく見るやつ。
(コード例は元記事を参照)
これ、(略) に (略) が入ったら終わる。自動化パイプラインはユーザー入力をそのままファイル名にすることが多いから、外部から仕込まれるリスクが現実的にある。
正しい書き方は引数をリストで渡す。
(コード例は元記事を参照)
シェルを経由しないので、スペースや特殊文字はそのまま引数として扱われる。(略) が必要になるのは、パイプ((略))やリダイレクト((略))をシェルに処理させたいときだけだと思っていい。
エラーを握りつぶさない——check=True と例外処理
(コード例は元記事を参照)
launchdで定期実行するスクリプトは、失敗を無視したまま次のステップに進む。それが一番たちが悪い。
(略) を付けると、コマンドが非ゼロで終了したときに (略) を投げてくれる。
(コード例は元記事を参照)
(略) で stdout/stderr を両方キャプチャ、(略) でバイト列じゃなく文字列として受け取れる。これをセットで覚えておくと後が楽だ。
タイムアウトを必ず設定する
これも見落としやすい。
(コード例は元記事を参照)
(略) を設定しないと、ネットワーク待ちや外部ツールのフリーズでプロセスが永久にブロックする。launchdで動かすジョブは誰も見ていないから、ゾンビのまま次の起動時刻まで居座る。
タイムアウト時は (略) が飛ぶ。
(コード例は元記事を参照)
僕のパイプラインでは、Ollamaへのリクエストやffmpeg変換に長めの (略) を、APIコール系には (略) を設定するようにしている。
出力を逐次読みたいとき——Popen を使う
(略) は完了まで待つ。長時間走るコマンドの出力をリアルタイムで処理したいなら (略) を使う。
(コード例は元記事を参照)
launchdのジョブで動かすとき、長時間のffmpeg変換で進捗を確認したくてこのパターンを使った。(略) をイテレートすると行単位で読める。
ただし (略) はリソース管理を自分でやる必要があるので、(略) ブロックで必ずラップする。
環境変数を安全に渡す
外部コマンドにAPIキーを渡すとき、コマンドライン引数に含めると (略) で丸見えになる。環境変数経由が安全だ。
(コード例は元記事を参照)
(略) で現在の環境変数をコピーして、必要なキーを追加する。これで親プロセスの環境変数も子プロセスに引き継がれる。
逆に特定の環境変数を「除外」したいケースもある。僕のパイプラインでは (略) を呼ぶとき、APIキーが環境変数にあるとサブスクじゃなくAPIクレジットを消費してしまう問題があった。
(コード例は元記事を参照)
これで Claude Code がサブスク経由で動く。実際に気づくまで数日クレジットを無駄にした。
作業ディレクトリを明示する
(コード例は元記事を参照)
(略) を指定しないと、スクリプトを起動したディレクトリで実行される。launchdから呼んだとき、作業ディレクトリが (略) だったりして相対パスが全部崩れることがある。
明示するのが一番手間が少ない。
実用ラッパー関数——パイプライン全体で使い回す
毎回同じ引数を書くのが面倒なので、ラッパーを1つ作って使い回している。
(コード例は元記事を参照)
呼び出し側はシンプルになる。
(コード例は元記事を参照)
まとめ
パターンをまとめると、
- (略) を使うなら理由を言語化できるときだけ
- (略) で失敗を必ず検知する
- (略) を省略しない
- (略) + (略) はセットで
- APIキー等は環境変数経由、(略) で見えない場所に
- (略) は明示する
自動化スクリプトは「誰も見ていない状態で動く」前提で書く必要がある。エラーを握りつぶしたり、タイムアウトを設定し忘れると、失敗したまま次のジョブが動いて整合性が壊れる。launchd + Python で組んでいると特にそれが刺さる。✨
失敗ログを眺めながら少しずつ堅牢にしていくのが、「完璧より継続」のやり方だと思っている。
(コード例は元記事を参照)