Skip to content
Go back

xargs vs while read: Which One and When

By SumGuy 5 min read
xargs vs while read: Which One and When

The Core Difference

Both xargs and while read iterate over input and run commands. But they solve slightly different problems, and picking the wrong one makes your scripts slower, fragile, or both.

while read is a bash loop. xargs is a tool that builds command lines. They’re fundamentally different approaches to the same problem.

while read: Simple, Slow, Safe(ish)

while read reads stdin line by line and runs a command on each. It’s straightforward:

Terminal window
cat file.txt | while read line; do
echo "Processing: $line"
done

Pros:

Cons:

Here’s where the subshell bites you:

Terminal window
count=0
ls -1 | while read file; do
((count++))
done
echo $count # Prints 0, not the actual count

The count increments inside the loop subshell, but outside? It’s still zero.

xargs: Fast, Parallel, Overkill Sometimes

xargs takes stdin and builds command-line arguments. Instead of looping, it bunches inputs together and runs the command fewer times:

Terminal window
find . -name "*.log" | xargs rm

That’s not a loop—it’s one or a few rm invocations with lots of arguments.

Pros:

Cons:

Safe xargs with find:

Terminal window
find . -name "*.log" -print0 | xargs -0 rm

The -print0 and -0 pair means: separate with null bytes instead of newlines. Handles filenames with spaces correctly.

Real Examples

Batch Download URLs (xargs wins)

Terminal window
cat urls.txt | xargs -I {} -P 4 curl -O {}

Here -P 4 means run 4 parallel curl jobs. With while read, you’d spawn a curl per iteration—way slower.

Process CSV Rows (while read is clearer)

Terminal window
while IFS=',' read id name email; do
echo "INSERT INTO users VALUES ($id, '$name', '$email')" | mysql db
done < data.csv

You need the field parsing anyway. xargs would be awkward here because each row becomes one command.

Count Files in Subdirs (while read, fix the subshell)

Terminal window
count=0
find . -mindepth 1 -maxdepth 1 -type d | while read dir; do
((count += $(ls "$dir" | wc -l)))
done
echo $count

Wait—subshell problem again. Fix it:

Terminal window
find . -mindepth 1 -maxdepth 1 -type d | while read dir; do
ls "$dir" | wc -l
done | awk '{sum += $1} END {print sum}'

Now the loop outputs numbers, and awk sums them outside the subshell.

The Decision Tree

Use while read if:

Use xargs if:

Bonus: Use GNU parallel if xargs is too basic:

Terminal window
cat urls.txt | parallel -j 4 curl -O {}

It’s xargs on steroids—better error handling, easier syntax, way more flexible.

The mapfile Alternative

If you’re using while read to populate an array, there’s a better way:

Terminal window
# Instead of this:
while read line; do
my_array+=("$line")
done < file.txt
# Use mapfile (bash 4+):
mapfile -t my_array < file.txt

mapfile reads everything into an array without a loop. No subshell issues. No IFS weirdness. Way faster.

Then iterate the array:

Terminal window
for item in "${my_array[@]}"; do
echo "Processing: $item"
done

find -exec vs xargs

find -exec is xargs with a different syntax:

Terminal window
# xargs
find . -name "*.log" | xargs rm
# find -exec (one command per file)
find . -name "*.log" -exec rm {} \;
# find -exec (batched, like xargs)
find . -name "*.log" -exec rm {} +

The + form batches files into one rm call — same behavior as xargs, but null-safe by default. Use it when you’re already in a find command and don’t want to pipe.

The Speed Difference

Real talk: on a list of 1000 files, xargs is 10–50x faster than while read. If you’re processing thousands of items, spend 2 minutes learning xargs -0. Your scripts will thank you.

But if you’re processing 50 lines? Readability wins. Go with while read.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Certificate Expiry: Monitor Before the 3 AM Call
Next Post
Where Environment Variables Actually Live in Linux

Related Posts