You tried rm *.log in a directory with two million log files, and bash shot back bash: /usr/bin/rm: Argument list too long. Annoying, but it's a solvable problem.
The fix isn't a bigger command. It's giving the shell fewer arguments at a time. You do that with find or xargs, which handle the batching for you.
Step-by-step fix
First, cd into the directory that's giving you trouble. Don't run these from a parent directory unless you want to sweep subdirectories too.
cd /var/log/myapp
Now, before you delete anything, list what you're about to remove. This is the step most people skip, and it's the step that saves your job. Use this command:
find . -maxdepth 1 -type f -name '*.log' | head -20
You should see a list of filenames scroll by, one per line, all ending in .log. If you see nothing, your pattern is wrong — stop and fix it before going further. If you see files you don't want deleted, adjust the -name pattern.
Once you're happy with the list, delete them with find's built-in -delete:
find . -maxdepth 1 -type f -name '*.log' -delete
After you press Enter, the shell will sit there for a while — sometimes minutes — with no output. That's normal. find is walking the directory and unlinking each file. When it returns to the prompt, run:
ls *.log | wc -l
You'll likely get -bash: /usr/bin/ls: Argument list too long again if any files remain, which itself tells you the delete didn't finish. If you get a number back, that's your remaining count. Ideally it's 0.
If you'd rather pipe to rm — say you want to log what's being deleted — use xargs instead:
find . -maxdepth 1 -type f -name '*.log' -print0 | xargs -0 rm -f
Same result. The -print0 and -0 combo is important: it uses null bytes as separators so filenames with spaces or newlines don't blow up the command.
Why this works
The error comes from execve(2), the syscall the shell uses to launch rm. The kernel enforces a limit on the total size of the argument list — historically ARG_MAX, usually 2 MB on modern Linux, though it's MAX_ARG_STRLEN * number_of_args in practice. When bash expands *.log into two million filenames and tries to hand them to rm in one shot, it blows past that limit. The kernel returns E2BIG, and bash translates that to Argument list too long.
find never passes the full list. It calls unlink() directly on each file, one at a time. No argument list, no limit. xargs does something similar: it splits the input into chunks that fit under ARG_MAX and runs rm once per chunk. Both approaches sidestep the kernel restriction instead of fighting it.
You might wonder why rm -rf directory/ works fine on a directory with millions of files. It does, because rm -rf on a directory walks it internally. The limit only bites when the shell has to expand a glob into an argument list.
Less common variations
You'll hit E2BIG in a few other spots, and the fix is the same shape every time:
grepacross thousands of files:grep 'error' *.txtfails. Usegrep -r 'error' .orfind . -name '*.txt' -exec grep -H 'error' {} +. The+at the end of-execbatches arguments automatically, unlike the older\;.- Permission changes on a huge tree:
chmod 644 *.phpfails in a directory with half a million PHP files (a real scenario on shared hosting after a compromised upload). Usefind . -maxdepth 1 -name '*.php' -exec chmod 644 {} +. - Clearing PHP session files:
/var/lib/php/sessionscan accumulate over a millionsess_*files if garbage collection is misconfigured. Same fix:find /var/lib/php/sessions -type f -name 'sess_*' -delete. - Deleting an enormous directory itself: if
rm -rf /path/to/dirhangs or runs for hours,rsyncis faster:mkdir /tmp/empty && rsync -a --delete /tmp/empty/ /path/to/dir/. It uses the same unlink path but skips some metadata stat calls. Thenrmdirthe empty shell. Argument list too longfrom a non-rmbinary: any tool that takes many filenames is vulnerable.tar czf backup.tar.gz *.logwill fail on a huge directory. Usetar czf backup.tar.gz --files-from <(find . -maxdepth 1 -name '*.log').
Prevention
The real fix is not letting a single directory grow into the millions of files in the first place. A few habits that pay off:
- Rotate logs aggressively. Configure
logrotatewith a lowrotatecount (7 or 14, not 365) and amaxsizedirective. A directory with daily logs from the last two weeks never gets out of hand. - Set a session garbage-collection policy for PHP, or whatever framework you run. Default
session.gc_maxlifetimeof 1440 seconds is fine, but make sure the GC is actually running (it's probabilistic, so low-traffic sites can skip it for weeks). - Use sharded directories for anything user-generated — uploads, caches, mail queues. Hash the filename and put it in a subdirectory like
a3/,b7/. You'll never hit the limit because no single directory grows past a few thousand entries. - Monitor inode counts on production servers.
df -itells you how close you are to running out. A directory with 5 million files isn't just slow to delete — it slows downls, backups, and anything that stats it. - Prefer
find -deleteoverrm *in scripts. It's idempotent, safe with null bytes, and doesn't care how many files match.
If you're already sitting on a directory with tens of millions of files, deleting it can take hours. Consider whether it's faster to reformat the volume or restore from a snapshot. There's no shame in that — it's often the right call on a busy production system.