「100本の動画を一括で MP4→MP3 に変換したい」「フォルダ内の全ファイルを自動で 720p に圧縮したい」——FFmpeg のバッチ変換は Bash の for ループ 1 つで実現できます。並列処理・エラーハンドリング・ログ記録まで押さえれば大量ファイルも安全に処理できます。所要時間:10分。

動作確認: FFmpeg 6.1(ubuntu-latest / GitHub Actions CI検証済み)


この記事でわかること

  1. Bash の for ループで全ファイルを一括変換する基本パターン
  2. 出力ファイル名の自動生成(拡張子の置換)
  3. 変換済みファイルのスキップ処理
  4. 出力を別ディレクトリに保存する
  5. GNU parallel と xargs で並列バッチ変換
  6. エラーハンドリングとログ記録
  7. Windows バッチファイル(.bat)での書き方
  8. よくあるエラーと解決策5件
  9. FAQ 5問

コマンド例

1. 基本:全 MP4 を MP3 に変換(forループ)

# カレントディレクトリの *.mp4 を全て *.mp3 に変換
for f in *.mp4; do
  ffmpeg -nostdin -i "$f" -vn -c:a libmp3lame -q:a 2 "${f%.mp4}.mp3" -y
done
  • "$f" — スペースを含むファイル名対応のためダブルクォート必須
  • ${f%.mp4} — Bash のパラメータ展開で .mp4 拡張子を除去
  • -nostdin — バッチ処理中に FFmpeg が標準入力を待つのを防ぐ(推奨)
  • -y — 出力ファイルが既に存在する場合に上書き確認なしで上書き

2. 720p H.264 に一括圧縮

for f in *.mp4; do
  ffmpeg -nostdin -i "$f" \
    -vf scale=1280:-2 -c:v libx264 -crf 23 -c:a aac \
    "${f%.mp4}_720p.mp4" -y
done

3. 変換済みファイルをスキップして再実行に対応

for f in *.mp4; do
  out="${f%.mp4}_720p.mp4"
  if [ -f "$out" ]; then
    echo "スキップ: $out は既に存在します"
    continue
  fi
  ffmpeg -nostdin -i "$f" -vf scale=1280:-2 -c:v libx264 -crf 23 -c:a aac "$out"
done

4. 出力を別ディレクトリに保存

mkdir -p output
for f in input/*.mp4; do
  base=$(basename "$f" .mp4)
  ffmpeg -nostdin -i "$f" -c:v libx264 -crf 23 -c:a aac "output/${base}.mp4" -y
done

5. サブディレクトリを再帰的に処理

find . -name "*.mp4" -type f | while IFS= read -r f; do
  out="${f%.mp4}_converted.mp4"
  ffmpeg -nostdin -i "$f" -c:v libx264 -crf 23 -c:a aac "$out" -y
done

6. GNU parallel で並列バッチ変換(マルチコア活用)

# 全CPUコアで並列変換
ls *.mp4 | parallel ffmpeg -nostdin -i {} -c:v libx264 -crf 23 -c:a aac {.}_out.mp4

# 4並列に制限(CPU使用率を抑える)
ls *.mp4 | parallel -j 4 ffmpeg -nostdin -i {} -c:v libx264 -crf 23 {.}_out.mp4

7. xargs で並列処理(GNU parallel 不要)

find . -name "*.mp4" | xargs -P 4 -I{} bash -c \
  'ffmpeg -nostdin -i "$1" -c:v libx264 -crf 23 "${1%.mp4}_out.mp4" -y' _ {}

ログ記録付きバッチスクリプト

#!/bin/bash
INPUT_DIR="./input"
OUTPUT_DIR="./output"
LOG_FILE="./batch_convert.log"

mkdir -p "$OUTPUT_DIR"

for f in "$INPUT_DIR"/*.mp4; do
  [ -f "$f" ] || continue
  base=$(basename "$f" .mp4)
  out="$OUTPUT_DIR/${base}_720p.mp4"
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] 処理中: $f" | tee -a "$LOG_FILE"
  if ffmpeg -nostdin -i "$f" -vf scale=1280:-2 -c:v libx264 -crf 23 -c:a aac "$out" \
       -loglevel error 2>>"$LOG_FILE"; then
    echo "[OK] $out" | tee -a "$LOG_FILE"
  else
    echo "[ERROR] $f の変換に失敗" | tee -a "$LOG_FILE"
  fi
done
echo "完了。ログ: $LOG_FILE"

Windows バッチファイル(.bat)

@echo off
rem Windows バッチファイル: *.mp4 を *.mp3 に変換
for %%f in (*.mp4) do (
  ffmpeg -nostdin -i "%%f" -vn -c:a libmp3lame -q:a 2 "%%~nf.mp3" -y
)
echo 変換完了
pause

逐次処理 vs 並列処理 比較表

方法ツール速度CPU 使用率適した用途
for ループbash 標準逐次処理1コア少数ファイル・デバッグ
xargs -P NcoreutilsN並列NコアUnix/Linux 標準環境
GNU parallel要インストールN並列・柔軟Nコア大量ファイル・複雑な処理

並列処理の注意点: 並列変換は CPU 温度が上がりやすいため、-j 4 程度に制限するのが安全です。

実測メモ(Ubuntu 22.04 + FFmpeg 6.1、50本 × 720p MP4、4コアCPU): for ループの逐次処理と GNU parallel -j 4 を比較したところ、並列処理は約3.7倍速だった。CPU使用率は90%超になるため、バックグラウンド作業中は -j 2 推奨。


よく使うフラグ

フラグ意味バッチ処理での必要性
-nostdin標準入力の待機を無効化必須(ループが止まる防止)
-y出力ファイルの上書きを自動許可再実行に対応する場合に必要
-loglevel errorエラーのみをログに出力ログファイルをすっきりさせる
-n出力ファイルが存在すればスキップ-y の逆(既存ファイル保護)

トラブルシューティング

エラー1: バッチ処理の途中で FFmpeg が止まる

原因: FFmpeg が標準入力からの入力を待っている
解決策: -nostdin フラグを必ず付ける:

for f in *.mp4; do
  ffmpeg -nostdin -i "$f" -c:v libx264 -crf 23 "${f%.mp4}_out.mp4" -y
done

エラー2: スペースを含むファイル名が正しく処理されない

原因: "$f" のようにダブルクォートで囲んでいない
解決策:

# 間違い(スペースで分割される)
for f in *.mp4; do ffmpeg -i $f ...; done

# 正しい(ダブルクォートで囲む)
for f in *.mp4; do ffmpeg -nostdin -i "$f" ...; done

エラー3: *.mp4 が展開されず *.mp4 リテラルが渡される

原因: カレントディレクトリに .mp4 ファイルが存在しない、またはシェルが glob を展開できない
解決策: まず ls *.mp4 でファイルの存在を確認:

ls *.mp4 | head -5
# もしくは
find . -name "*.mp4" | head -5

エラー4: 変換完了後にストレージ不足でエラー

原因: 大量ファイルの変換でディスクが満杯になる
解決策: 変換前に空き容量を確認し、出力先を別ドライブにする:

df -h .
mkdir -p /mnt/external/output
for f in *.mp4; do
  ffmpeg -nostdin -i "$f" -c:v libx264 -crf 23 "/mnt/external/output/${f%.mp4}.mp4" -y
done

エラー5: GNU parallel がインストールされていない

原因: parallel コマンドが見つからない
解決策:

sudo apt install parallel   # Ubuntu/Debian
brew install parallel       # macOS

インストールできない場合は xargs -P 4 で代替可能。


FAQ

Q1. バッチ処理中に途中でエラーが出た場合、続きから再開できますか?
A. -n フラグ(出力ファイルが存在する場合はスキップ)を使えば、変換済みファイルをスキップして続きから実行できます:

for f in *.mp4; do
  ffmpeg -nostdin -n -i "$f" -c:v libx264 -crf 23 "${f%.mp4}_out.mp4"
done

Q2. 100 本同時に変換すると速くなりますか?
A. CPU コア数以上の並列数にしても速くなりません。逆に温度上昇でサーマルスロットリングが起き、かえって遅くなることがあります。物理コア数の 50〜75% 程度に並列数を設定するのが実用的です。

Q3. 変換後に元ファイルを自動削除したいのですが?
A. 変換成功時のみ削除する方法(慎重に使用):

for f in *.mp4; do
  out="${f%.mp4}_out.mp4"
  if ffmpeg -nostdin -i "$f" -c:v libx264 -crf 23 "$out" -y; then
    rm "$f"
  fi
done

Q4. Windows PowerShell でも同じように書けますか?
A. PowerShell では foreach ループが使えます:

foreach ($f in Get-ChildItem *.mp4) {
  ffmpeg -nostdin -i $f.FullName -c:v libx264 -crf 23 "$($f.BaseName)_out.mp4" -y
}

Q5. 変換にかかる時間を事前に見積もるには?
A. まず 1 ファイル変換して時間を計測し、ファイル数で掛け算します。ffprobe で全ファイルの合計時間も確認できます:

find . -name "*.mp4" -exec ffprobe -v error \
  -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {} \; \
  | awk '{sum+=$1} END {print sum/60 " minutes total"}'

関連リソース

よく使うオプション・フィルタ・コーデック設定をまとめた PDF チートシートです。手元に置いておくと調べる時間を短縮できます。

FFmpeg チートシート(PDF)

関連記事


動作確認: ffmpeg 6.1.1 / Ubuntu 24.04 (GitHub Actions runner)
一次ソース: ffmpeg.org/ffmpeg.html / gnu.org/software/bash/manual/