In this episode you'll process hundreds of files at once with shell loops, run parallel conversions with find and xargs, and write safe, repeatable batch scripts.

So far each episode processed one file. But in the real world, media arrives in bulk: 500 raw clips from a recording session, thousands of files to reformat on every release, or one old archive folder to standardize. Running commands one by one for all those files isn't just boring — it's error-prone and time-consuming.
This episode turns FFmpeg from a manual tool into a batch machine: shell loops to process many files, clean output naming, parallel conversion, and scripts that are safe to run repeatedly. These are skills used directly in production.
The heart of batch processing in the shell is the for loop. Its style: for every file matching a pattern, run ffmpeg.
for file in *.mp4; do
ffmpeg -i "$file" -c:v libx264 "${file%.mp4}.mkv"
doneLet's break it down line by line. *.mp4 expands to all MP4 files in the current directory. Each file name is held in the file variable. The ffmpeg command is called with an output name derived from the variable — the %.mp4 part strips the extension, then we add .mkv after it. Result: video.mp4 becomes video.mkv, adik.mp4 becomes adik.mkv, and so on.
Tip
Always wrap variables in double quotes: ffmpeg -i "$file". File names containing spaces — like klip malam.mp4 — will be split into separate arguments without quotes, and the command fails immediately. Double quotes are a small habit with a big impact.
Output that overwrites the original file is a road to disaster. Get into the habit of writing results to clearly distinct names, or better yet, to a separate directory.
for file in *.mp4; do
ffmpeg -i "$file" -c:v libx264 "${file%.mp4}-h264.mp4"
doneAdding the -h264 suffix makes every output easily recognizable and never overwrites the source. For a separate output folder, create the directory first then point the results there:
mkdir -p converted
for file in *.mp4; do
ffmpeg -i "$file" -c:v libx264 "converted/${file}"
doneWith this pattern, the converted folder holds the results and the source folder stays clean — you can compare or recheck without fear of data loss.
A loop runs sequentially — one file finishes before the next starts. For folders with hundreds of files, this is slow. Modern CPUs have many cores; use them all with xargs -P, which runs many commands at once.
find . -name "*.mp4" -print0 | xargs -0 -P 4 -I {} ffmpeg -y -i {} -c:v libx264 {}.h264.mp4Let's break it down: find finds all MP4s and outputs them with a null separator (-print0) so names with spaces are safe. xargs -0 reads that list, -P 4 runs four ffmpeg processes at once, and -I {} substitutes {} with each file name. The result is two to four times faster than a sequential loop.
Warning
Parallelism doubles the machine load. -P 4 means four simultaneous conversions — adjust it to your core count (nproc). Never point output to the same folder as the input, and don't use output patterns that could overwrite each other — two processes writing to one file is a recipe for corrupted files.
A terminal loop is fine for one-off use. For repetitive work — e.g., a team's standard conversion pipeline — write a script that can be run any time with consistent results. Here is a pattern that teaches safe discipline.
#!/usr/bin/env bash
set -euo pipefail
src_dir="${1:?usage: $0 <source dir>}"
out_dir="${src_dir}/converted"
mkdir -p "$out_dir"
for file in "$src_dir"/*.mp4; do
[ -e "$file" ] || continue
name="$(basename "$file" .mp4)"
echo "converting: $file"
ffmpeg -y -i "$file" -c:v libx264 -c:a aac "$out_dir/${name}.mp4"
done
echo "done: $(find "$out_dir" -name '*.mp4' | wc -l) files"Let's see why each part exists:
set -euo pipefail — stop on error (-e), reject undefined variables (-u), detect failures in the middle of a pipeline (-o pipefail).[ -e "$file" ] || continue — skips glob patterns that don't match any file.-y — answers "yes" to automatic overwrites, so the script doesn't wait for confirmation.converted/ — the source is never touched.A few principles make a batch pipeline safe to run repeatedly (idempotent):
-y to always overwrite, or -n to never overwrite. Don't let FFmpeg ask interactively — the script will hang.-hide_banner and -loglevel error keep script logs clean; relevant errors still show.ffmpeg -hide_banner -loglevel error -i input.mp4 -c copy output.mp4With this quiet mode, the script output only contains messages from the echo statements you write — easy to read and easy to search in CI/CD logs.
Important
Run batches on a machine with enough resources, and always prepare realistic time and disk space. One minute of 1080p re-encode can take seconds to minutes on one core; a hundred files means that multiplied. Check disk space with df -h before launching a big batch.
In episode 9 you've turned FFmpeg into a batch machine: for loops for many files, output naming with shell variables, parallel conversion with find + xargs -P, and safe production scripts with -y, set -euo pipefail, and result verification.
The key takeaway: batch processing isn't about typing fast, it's about being safe. A script that can be rerun without fear of overwriting originals or stopping midway is an asset — and its two principles are simple: output always separate, and every failure clearly visible.
In the next episode 10 we'll stop processing video as video — you'll create thumbnails, frame images, and animated GIFs from video, complete with palette optimization so the GIFs look professional. See you in episode 10!