linuxのコマンドラインでargument list too longエラーが出た時の対処方法
ここではargument list too long(引数リストが長すぎます)エラーについての対処方法について解説します。
まずはコマンドラインについて確認しましょう。
コマンドに引数を渡すときは、下記のように記述します。
dir1が引数ですが、これが上限を超えるとargument list too longエラーとなります。
この上限はgetconf ARG_MAXコマンドで確認できます。
1
2
|
getconf ARG_MAX
2097152
|
手元の環境では2MBでした。
この値はコンパイル時に埋め込まれる値のため動的な変更はできません。
この問題が発生する時は大体がワイルドカードを使用したコマンドを実行した時だと思います。
ワイルドカードを使用すると、シェルが展開を行いls dir*
コマンドは下記のように置き換えられます。
もしdirディレクトリが100万個あったとすると、先ほどの上限は軽く超えてしまうためargument list too longエラーとなってしまいます。
対処方法:xargsを使用する
このように大量のディレクトリやファイルを扱う場合、どうすればよいでしょうか。
その答えはxargsコマンドを使用すればいい、です。
先ほどのコマンドをxargsコマンドを使った場合に置き換えると下記のようになります。
1
|
find . -maxdepth 1 -type d -name 'dir*' | xargs ls
|
xargsの動作は簡単に以下の流れになります。
- 標準入力から読み込んだ文字列を指定したコマンド(例だとls)の引数として与え作成する
- 作成したコマンドを実行する
上記例の場合は下記のコマンドを作成してくれます。
ですが、このままではまたargument list too longが起きるのでは?と思ってしまいますが、xargsはシステムが定めているコマンドラインの長さの限界に達するまで、長いものが作成される仕様となっているため、argument list too longエラーは発生しません。
つまり限界が来たら下記のように複数回のコマンド実行に置き換えられます。
1
2
|
ls dir1 dir2 dir3 ... dir1000 dir1001 dir1002
ls dir1002 dir1003 dir1004 ... dir2001 dir2002 dir2003
|
ちなみにxargsのコマンドラインの限界の長さは--show-limits
オプションで確認できます。
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.
|
Size of command buffer we are actually usingの項目がそれで手元の環境では約130kとなっていました。
システムの限界が2MBだったのに130kとなっているのはsオプションを指定しない場合のデフォルト値が130kになっているからです。
試しにsオプションで2MBを指定してみると変更されることが分かります。
1
2
3
4
|
xargs -s 2000000 --show-limits
...省略
Size of command buffer we are actually using: 2000000
...省略
|
ところで、getconf ARG_MAX
コマンドで取得した上限は2MBでした。
ではこの上限を超えた値、例えば3MBを指定するとどうなるのでしょうか。
答えは上限に補正される、です。
試しに指定してみると2MBとなり、上限に補正されていることが分かります。
1
2
3
4
|
xargs -s 3000000 --show-limits
...省略
Size of command buffer we are actually using: 2088784
...省略
|
実際に確認してみよう
xargsの利便性は分かってもテストしないと怖くて実行できないと思います。
ここではテスト環境を作成して実際に確認してみることにしましょう。
テスト環境を作成する
それではテスト環境を作成して実際にxargsコマンドの動作確認をしてみましょう。
ここではディレクトリが1000個、各ディレクトリにファイルが100個あるケースで実際にやってみることにします。
まずはテストケースのディレクトリとファイルを作成します。
今回はtestディレクトリに環境を作成することにしました。
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 ...省略
という形になります。
置き換える文字列に{}
を使っていますがaaa
やbbb
といった任意の文字列が使えます。ただし基本的に{}
を使うことになっており、混乱を避けるためにも特に理由がなければ{}
を使うことをお勧めします。
argument list too longを発生させてみる
少し話がそれましたが、テスト環境でargument list too longが発生するか確認してみましょう。
1
2
|
/test# ls **/*.txt
bash: /usr/bin/ls: Argument list too long
|
全ての*.txtファイルを取得しようとするとargument list too longが発生しました。
ちゃんと上限を超えているようですね。
.txtファイルを一つずつ取得してみる
では次に各ディレクトリの.txtファイルを一つずつ取得してみましょう。
1
2
3
|
/test# find . -type f -name '*.txt' | xargs ls -l > filelist.txt
/test# wc -l filelist.txt
100001 filelist.txt
|
lsコマンドの結果をファイルに出力し行数を数えるとちゃんと10万行になっている事が分かります。
これで大量のファイルを一つずつ処理できることが確認できましたが、実際に実行されるコマンドを事前に確認したいことがあると思います。
その場合はpオプションを使用します。
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.txt~file080.txtを削除してみましょう。
まずは念のため、まとめて削除できないことを確認してみます。
1
2
|
/test# rm **/file{000..080}.txt
bash: /usr/bin/rm: Argument list too long
|
やはりエラーが出て削除できませんね。
ファイル数が多すぎるようです。
では一つずつファイルを取得してxargsに削除コマンドを作成してもらい実行する形にしてみます。
今回はテストのためpオプションなしで確認せずに一気に削除します。
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.txt~file080.txtがちゃんと削除されていることが分かります。
最後に
xargsを使えば大量のファイルを扱う際にとても効率よくコマンドを実行できることが分かって頂けたかと思います。
他のオプションや詳しい使い方については下記ドキュメントを参照してください。
JM Project xargs
実行環境
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.
|
↓↓↓下記はアフィリンクです↓↓↓
ハードウェアスタートアップ企業のお話です。
ほのぼのとした世界観でとても読みやすいので息抜きに是非。
途中まで無料で読めます。