Fix "Argument list too long" on Linux

Quick fix

Let find handle the files instead of expanding * on the command line.

# instead of: rm /var/log/app/*.log
find /var/log/app -maxdepth 1 -type f -name '*.log' -delete
# instead of: mv *.jpg /archive/
find . -maxdepth 1 -name '*.jpg' -exec mv -t /archive/ {} +

The error looks like this (the program path varies):

bash: /usr/bin/rm: Argument list too long
-bash: /bin/mv: Argument list too long
zsh: argument list too long: rm

What the error means

Before running rm *.log, the shell expands *.log into every matching file name and passes them all as arguments to rm. The kernel limits the total size of arguments plus environment that a new program can receive. When the expanded list is bigger than that, execve() fails with E2BIG and the shell prints "Argument list too long". Nothing was deleted, moved or copied.

getconf ARG_MAX

Example output:

2097152

On Linux this is a quarter of the stack size limit (ulimit -s, 8 MiB by default), so typically 2 MiB. Tens of thousands of long paths fill it quickly. Separately, a single argument may not exceed 128 KiB.

Fix 1: find with -delete (removing files)

find walks the directory itself and never passes the whole list to another program:

# preview first
find /path/to/dir -maxdepth 1 -type f -name '*.tmp' | head
find /path/to/dir -maxdepth 1 -type f -name '*.tmp' | wc -l
# then delete
find /path/to/dir -maxdepth 1 -type f -name '*.tmp' -delete
Check before you delete

-delete is immediate and recursive unless you add -maxdepth 1. Run the same command without -delete first, and keep -delete as the last option: placed before -name, it deletes everything find visits.

Useful filters: -mtime +30 (older than 30 days), -size +100M, -user alice. The find cheat sheet lists many more.

Fix 2: find -exec ... + (mv, cp, chmod, chown)

With + at the end, find packs as many names as fit into each command and runs it as many times as needed, staying under the limit automatically:

# move
find . -maxdepth 1 -type f -name '*.jpg' -exec mv -t /archive/ {} +
# copy
find . -maxdepth 1 -type f -name '*.csv' -exec cp -t /backup/ {} +
# change permissions
find /srv/www -type f -exec chmod 644 {} +

-t DIR (GNU coreutils) puts the destination first, so the file names can come last where {} is. Using \; instead of + also works but starts one process per file, which is much slower for large sets.

Fix 3: xargs with null separators

xargs reads names from standard input and splits them into batches that fit. Always pair find -print0 with xargs -0 so names with spaces or newlines are safe:

find . -maxdepth 1 -name '*.log' -print0 | xargs -0 rm --
find . -maxdepth 1 -name '*.png' -print0 | xargs -0 -P 4 -n 100 optipng

-P 4 runs four processes in parallel; -n 100 limits each batch to 100 files. Without -0, file names with spaces get split into pieces and the wrong files can be affected.

If the names come from a glob rather than find, use bash's builtin printf, which does not hit the limit because no new program is executed:

printf '%s\0' *.log | xargs -0 rm --

Fix 4: a shell loop

Loops are expanded inside the shell, so for f in * works with any number of files. It runs one command per file, which is slow for hundreds of thousands, but is easy to adapt when each file needs different handling:

for f in *.txt; do
    mv -- "$f" "archive/${f%.txt}.bak"
done

For renaming large batches, the bulk rename script handles this safely.

Fix 5: operate on the directory, not the files

If you want everything in a directory gone or moved, skip the glob entirely:

# move the whole directory
mv /data/incoming /data/archive/incoming-2026-10-16
# copy contents including hidden files
cp -a /src/dir/. /dest/dir/
# rsync handles any number of files
rsync -a /src/dir/ /dest/dir/

Deleting the parent directory with rm -rf /path/to/dir also avoids the limit, because rm traverses it itself; double-check the path before running it.

Other situations

  • ls *.jpg | wc -l fails the same way. Use find . -maxdepth 1 -name '*.jpg' | wc -l.
  • grep pattern * in a huge directory: use grep -r pattern . or grep -r --include='*.log' pattern ..
  • tar czf out.tgz *: archive the directory (tar czf out.tgz dir/) or feed names with find ... -print0 | tar --null -T - -czf out.tgz.
  • Huge environment: a very large exported variable eats into the same space, so even short commands fail. Check with env | wc -c.
  • Build systems and compilers with very long command lines usually support response files (@file) for this reason.

Distro notes

The limit is a kernel feature and behaves the same on Ubuntu, Debian, Fedora, Arch and openSUSE. The GNU find, xargs and mv -t used above are standard on all of them. On BusyBox-based systems (Alpine, some containers), -delete and -exec + are supported in recent versions, but mv -t may not be; use xargs -0 sh -c 'mv "$@" /dest/' _ there.

How to prevent it

  • Write scripts with find ... -exec ... + or -print0 | xargs -0 from the start; they work for 10 files and 10 million.
  • Rotate or prune directories that accumulate files (logs, caches, uploads) with a timer or logrotate, so they never grow that large.
  • Avoid storing millions of files in a single directory; split by date or hash prefix.

The bash scripting tutorial covers globbing and quoting, which are behind most of these problems.

Frequently asked questions

How do I delete a huge number of files on Linux?

Use find instead of a glob: find /path -maxdepth 1 -type f -name '*.log' -delete. It deletes files one by one without building a giant argument list.

What is the maximum argument length on Linux?

Run getconf ARG_MAX; on most systems it is 2097152 bytes (2 MiB), a quarter of the default 8 MiB stack limit. Arguments and environment variables share that space, and a single argument cannot exceed 128 KiB.

Why does for f in * work when rm * fails?

The limit applies only when the kernel starts a new program. A for loop and other shell builtins expand the glob inside bash, so no exec happens until each individual command runs.

Can I raise ARG_MAX?

Indirectly, with ulimit -s (a bigger stack raises the limit), but that is a workaround. Using find or xargs scales to any number of files.

Spotted a mistake or a command that no longer works? Send a correction — pages are re-checked when reports come in.