まずはArgument list too long
をgoogleで調べてみる。3歳でもできることだ。
出てきたのはrmやcpのパスをワイルドカードで指定したらエラーになった、とかいう記事ばかりだ。ホンモノの奴らだ。5ページまでみてもホンモノの奴らしかいない。
findしてxarg rmすればいいですよ。いかがでしたか?
じゃない。こっちはdirnameで死んでるんだ。引数一個ポッキリのやつだ。
だがこれくらいで挫けてちゃこの仕事は務まらねえ。俺はgithubのbashのミラーを開くと、URLを確認してbash 4.4のtarballを問題が起きているVMへとコピーした。いろんなコマンドで問題が起きている以上、問題はshellかOSにあるはずだ。まずはshellから調べよう。
./configure && makeし、/bin/bashにsymlinkを張る。外部コマンドの実行はexecveとかのはずだ。検索してみるとshell_execveという、そのものずばりの関数が見つかった。この関数にくるだろうか。printfで適当にargs引数を出力してみる。
出力がない。ハズレかな?いや、bashだしstdoutやstderrが付け替えられててもおかしくない。/tmp/logあたりにファイルを開きfprintfすると、実際にこの関数で外部コマンドを実行していることが確認できた。
idx:0, dirname
idx:1, (EDITED)/root.sh
やはり引数は1つしか来てない。そうすると次はシステムコールexecveがエラーになっているのか、bashの中で何か間違えてArgument list too long
を出しているかだ。errnoのログを出してみるとerrno:7となっていた。つまりexecveでerrno=7(E2BIG)で失敗しているってことだ。
E2BIGは聞いたことがある。まさにArgument list too long
の時のerrnoだったはず。振り出しに戻った気がして気が滅入るが、気を取り直してexecveのmanページでE2BIGを見てみる。
おお、E2BIGは実際には環境変数 (envp) と引き数リスト (argv) の合計バイト数が大き過ぎる
ことを表すらしい。envを出力してみることにする。
idx:0, dirname
idx:1, (EDITED)/root.sh
....
env:36, CI_CHANGE_SET=.circleci/config.yml
foo/bar/baz.txt
...改行を含む超長いリスト...
fuga/hoge.png
env:37, (EDITED)
目の前がパッと開けた気がした。なるほど。コミットで変更されたファイルを事前計算して記録している環境変数が長すぎるのか...
実際、
too longなリストはこのコミットで消された数千のファイル達
だったわけだ。
結局change setの事前計算で類似のパスを集約してサイズを小さくすることでこの問題は解決した。この記事が5ページまでホンモノの奴らばかりのArgument list too long
のgoogle検索結果で戸惑う人の助けになると嬉しい。