linuxのコマンドラインでargument list too longエラーが出た時の対処方法

JavaScriptを有効にしてください

前書き

コマンドラインで大量のファイルやディレクトリを扱う際に「argument list too long」というエラーに直面することがあります。
このエラーは、システムが扱える引数の上限を超えた場合に発生します。
その原因と対処法について解説します。

エラーの原因

コマンドラインでコマンドに引数を渡す際、例えばls dir1のように記述します。
ここでdir1が引数となりますが、この引数の数がシステムの許容上限を超えると「argument list too long」エラーが発生します。

この上限値はgetconf ARG_MAXコマンドで確認でき、多くの環境では約2MBと設定されています。
この値はシステムのコンパイル時に定められるため、動的に変更することはできません。

試しに確認してみると、

cmd
1
2
getconf ARG_MAX
2097152

手元の環境では2MBと表示されました。

この問題が発生する時は大体がワイルドカードを使用したコマンドを実行した時だと思います。

cmd
1
ls dir*

上記のようにワイルドカードを使用すると、シェルが展開を行いls dir*コマンドは下記のように置き換えられます。

cmd
1
ls dir1 dir2 dir3 ...

もしdirディレクトリが100万個あったとすると、先ほどの上限は軽く超えてしまうためargument list too longエラーとなってしまいます。

対処方法その1 xargsコマンドの使用

大量のファイルやディレクトリを扱う場合、xargsコマンドが非常に有効です。
xargsは標準入力から受け取ったデータを元に、指定したコマンドの引数として渡し、実行することができます。
例えば、以下のように使用します。

cmd
1
find . -maxdepth 1 -type d -name 'dir*' | xargs ls

xargsの動作は簡単に以下の流れになります。

  • 標準入力から読み込んだ文字列を指定したコマンド(例だとls)の引数として与え作成する
  • 作成したコマンドを実行する

上記例の場合は下記のコマンドを作成してくれます。

cmd
1
ls dir1 dir2 dir3 ...

ですが、このままではまたargument list too longが起きるのでは?と思ってしまいますが、xargsはシステムが定めているコマンドラインの長さの限界に達するまで長いものが作成される仕様となっているため、argument list too longエラーは発生しません。
つまり限界が来たら下記のように複数回のコマンド実行に置き換えられます。

cmd
1
2
ls dir1 dir2 dir3 ... dir1000 dir1001 dir1002
ls dir1003 dir1004 ... dir2001 dir2002 dir2003

ちなみにxargsのコマンドラインの限界の長さは--show-limitsオプションで確認できます。

cmd
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
xargs --show-limits
Your environment variables take up 6320 bytes
POSIX upper limit on argument length (this system): 2088784
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 2082464
Size of command buffer we are actually using: 131072
Maximum parallelism (--max-procs must be no greater): 2147483647

Execution of xargs will continue now, and it will try to read its input and run commands; if this is not what you wanted to happen, please type the end-of-file keystroke.
Warning: echo will be run at least once.  If you do not want that to happen, then press the interrupt keystroke.

6行目のSize of command buffer we are actually usingの項目がそれで手元の環境では約130kとなっていました。
システムの限界が2MBだったのに130kとなっているのはsオプションを指定しない場合のデフォルト値が130kになっているからです。
試しにsオプション2MBを指定してみると変更されることが分かります。

cmd
1
2
3
4
xargs -s 2000000 --show-limits
...省略
Size of command buffer we are actually using: 2000000
...省略

ところで、getconf ARG_MAXコマンドで取得した上限は2MBでした。
ではこの上限を超えた値、例えば3MBSize of command buffer we are actually usingの項目に指定するとどうなるのでしょうか。
答えは上限に補正される、です。

試しに指定してみると2MBとなり、上限に補正されていることが分かります。

cmd
1
2
3
4
xargs -s 3000000 --show-limits
...省略
Size of command buffer we are actually using: 2088784
...省略

xargsの動作を実際に確認してみよう

xargsの利便性は分かってもテストしないと怖くて実行できないと思います。
ここではテスト環境を作成して実際に動作を確認してみましょう。

テスト環境を作成する

ではテスト環境を作成して実際にxargsコマンドの動作を確認してみましょう。

ここではディレクトリが1000個、各ディレクトリにファイルが100個あるケースで実際にやってみます。
まずはテストケースのディレクトリとファイルを作成します。
今回はtestディレクトリに環境を作成することにしました。

cmd
1
2
3
4
/# mkdir test
/# cd test
/test# mkdir dir{0001..1000}
/test# find . -maxdepth 1 -type d -name 'dir*' | xargs -I {} touch {}/file{001..100}.txt

何やら4行目で早速xargsを使っていますが、この例では既にargument list too longが発生する状態のため使用せざるを得ません。

ここで使用しているIオプションは標準入力から渡される文字列を任意の場所に埋め込みたい場合に使用します。
xargs lsのように、何も指定しないとlsの後ろにしか追加されないため、コマンドの指定の位置に埋め込みたい場合はIオプションを使用します。
この場合、{}dir0001に置き換えられることになり実際は、
touch dir0001/file{001..100}.txt dir0002/file{001..100}.txt ...省略
という形になります。

置き換える文字列に{}を使っていますがaaabbbといった任意の文字列が使えます。ただし基本的に{}を使うことになっており、混乱を避けるためにも特に理由がなければ{}を使うことをお勧めします。

argument list too longを発生させてみる

少し話がそれましたが、テスト環境でargument list too longが発生するか確認してみましょう。

cmd
1
2
/test# ls **/*.txt
bash: /usr/bin/ls: Argument list too long

全ての*.txtファイルを取得しようとするとargument list too longが発生しました。
ちゃんと上限を超えているようですね。

.txtファイルを一つずつ取得してみる

では次に各ディレクトリの.txtファイルを一つずつ取得してみましょう。

cmd
1
2
3
/test# find . -type f -name '*.txt' | xargs ls -l > filelist.txt
/test# wc -l filelist.txt
100001 filelist.txt

lsコマンドの結果をファイルに出力し行数を数えるとちゃんと10万行になっている事が分かります。(100001となっているのは最後の改行によるもの)

これで大量のファイルを1つずつ処理できることが確認できましたが、実際に実行されるコマンドを事前に確認したいことがあると思います。
その場合はpオプションを使用します。

cmd
1
2
/test# find . -type f -name '*.txt' | xargs -p ls -l
ls -l ./dir0586/file022.txt ...省略... ./dir0533/file071.txt ?...

実行するととてつもなく長いコマンドが表示された後に?...と表示され入力待ちになります。
ここでyまたはYを入力すると表示されたコマンドが実行され、そのままenterを押下するとスキップされて次のコマンドが表示されるようになっています。
特に削除するようなコマンドを実行する時はpオプションを指定して、想定した引数になっているか確認してから実行するようにしましょう。

実際に削除してみる

ここまで確認できれば大量ファイルの削除は簡単です。
各ディレクトリのfile001.txtfile080.txtを削除してみましょう。
まずは念のため、まとめて削除できないことを確認してみます。

cmd
1
2
/test# rm **/file{000..080}.txt
bash: /usr/bin/rm: Argument list too long

やはりエラーが出て削除できませんね。
引数が多すぎるようです。

では1つずつファイルを取得してxargsに削除コマンドを作成してもらい実行する形にしてみます。
今回はテストのためpオプションなしで確認せずに一気に削除します。

cmd
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/test# find . -maxdepth 1 -type d -name 'dir*' | xargs -I {} rm {}/file{001..080}.txt
/test# find . -type f -name '*.txt' | xargs ls -l > filelist.txt
/test# wc -l filelist.txt
20001 filelist.txt
/test# ls -l dir0001
total 0
-rw-r--r-- 1 root root 0 Dec 23 08:36 file081.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file082.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file083.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file084.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file085.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file086.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file087.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file088.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file089.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file090.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file091.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file092.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file093.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file094.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file095.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file096.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file097.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file098.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file099.txt
-rw-r--r-- 1 root root 0 Dec 23 08:36 file100.txt

削除後に確認してみるとfile001.txtfile080.txtがちゃんと削除されていることが分かります。

対処方法その2. findコマンドの-execオプションの使用

xargsの代わりに、findコマンドの-execオプションを使用してもargument list too longの問題を解決できます。
-execオプションを利用すると、findが見つけた各ファイルに対して直接コマンドを実行することができます。

cmd
1
find . -type f -name '*.txt' -exec ls -l {} \;

上記は下記のようなイメージになります。

cmd
1
2
3
4
ls -l 1.txt
ls -l 2.txt
ls -l 3.txt
...

こちらでもargument list too longのエラーは回避できますが、一つ一つのファイルに対してコマンドを実行することになりxargsと比べるとかなり非効率です。
ですが、まとめてファイルを処理する必要がないケースではxargsと比べてfind-execオプションを使用した方が簡潔に記載できるメリットがあります。

例えば下記のようなケースではfind-execオプションの組み合わせが有効です。

1. 特定のファイルを見つけて削除する

特定の拡張子を持つファイルを探して削除する必要がある場合、-execオプションを使用すると、見つかった各ファイルに対して直接rmコマンドを実行できます。

cmd
1
find /path/to/directory -type f -name "*.tmp" -exec rm {} \;

このコマンドは/path/to/directory内の.tmp拡張子を持つすべてのファイルを検索し、それぞれを削除します。
xargsを使用するよりも直接的で、シンプルなタスクに対してはより読みやすい解決策です。

2. ファイルに対して特定のコマンドを実行する

見つけたファイルに対してコマンドを実行する必要がある場合、例えばファイルのパーミッションを変更する場合、-execオプションを使うことで、各ファイルに対して直接chmodコマンドを実行できます。

cmd
1
find /path/to/directory -type f -name "*.sh" -exec chmod +x {} \;

3. 複数のコマンドを連鎖させる

-execオプションを使用すると、見つけたファイルに対して複数のコマンドを連鎖させて実行することも可能です。
例えば、特定のファイルを見つけて内容を表示した後に削除する場合、以下のように記述できます。

cmd
1
find /path/to/directory -type f -name "*.log" -exec cat {} \; -exec rm {} \;

このコマンドは、.logファイルを検索し、それぞれの内容を表示(cat)した後に削除(rm)します。
この方法は、複数のステップを一つのコマンドライン操作で実行したい場合に有効です。

まとめ

argument list too longエラーは、コマンドラインで大量のファイルを扱う際によく遭遇する問題です。
しかし、xargsfind-execオプションを適切に使用することで、この問題を回避しつつ作業を効率化することが可能です。
さらに詳しい情報やオプションについては、各コマンドのマニュアルを参照してください。
JM Project find
JM Project xargs

記事内容でご不明な点がございましたら、下記Xアカウントにお気軽にDMください。
もふもふ-Xアカウント

実行環境

cmd
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/test# cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

/test# xargs --version
xargs (GNU findutils) 4.7.0
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Eric B. Decker, James Youngman, and Kevin Dalley.

スポンサーリンク

共有

もふもふ
著者
もふもふ
プログラマ。汎用系→ゲームエンジニア→Webエンジニア→QAエンジニア