ファイル管理をしていると「拡張子がついていないファイル」に遭遇することがあります。ログファイル・設定ファイル・旧システムからのエクスポートデータなどに多く見られます。
この記事では バッチファイルで拡張子のないファイルに一括で拡張子を追加する方法 を体系的に解説します。基本の1行コマンドから「サブフォルダ再帰」「ファイル内容による種別自動判定」「安全なドライラン実行」まで網羅します。
- カレントフォルダの拡張子なしファイルに拡張子を一括追加する方法(基本)
- サブフォルダ内のファイルも含めて再帰的に処理する方法(for /r)
- 特定フォルダを指定して処理する方法
- ファイル内容からテキスト/バイナリを判別して拡張子を自動付与する方法
- 実行前にドライランで処理対象を確認する方法
- 既存ファイルとの重複チェックで安全に名前変更する方法
- 落とし穴5選・実践例3本・FAQ6問
1. 基本:カレントフォルダの拡張子なしファイルに拡張子を追加
拡張子なしファイルとは、ファイル名にドット(.)が含まれない、または末尾がドットで終わるファイルのことです。
1-1. カレントフォルダのみ(最もシンプル)
@echo off
setlocal enabledelayedexpansion
:: *. : ドットで終わるファイル(拡張子なし)にマッチ
for %%f in (*.) do (
echo 処理中: %%f
ren "%%f" "%%~nf.txt"
)
echo 完了
endlocal
%%~nf は %%f からドットを除いたファイル名部分(拡張子なし名前)を取得します。例えば %%f が report. なら %%~nf は report になります。
1-2. 処理結果の確認付き(エラーハンドリング)
@echo off
setlocal enabledelayedexpansion
set "COUNT=0"
set "ERR_COUNT=0"
for %%f in (*.) do (
set "NEWNAME=%%~nf.txt"
ren "%%f" "!NEWNAME!"
if !ERRORLEVEL! equ 0 (
echo [OK] %%f -> !NEWNAME!
set /a COUNT+=1
) else (
echo [ERROR] %%f リネーム失敗
set /a ERR_COUNT+=1
)
)
echo.
echo ===== 完了: 成功 !COUNT! 件 / 失敗 !ERR_COUNT! 件 =====
endlocal
2. ren コマンドと for ループの仕組み
拡張子追加に使うコマンドの基本を理解しておくと応用が効きます。
2-1. ren コマンドの書式
:: ren 変更前ファイル名 変更後ファイル名 :: ※変更後のファイル名にはパスを含められない(同フォルダ内のリネームのみ) :: 拡張子なしファイル「report」に .txt を追加 ren "report." "report.txt" :: ワイルドカード * で名前部分を保持しつつ拡張子だけ変える ren "*." "*.txt" :: → カレントフォルダの拡張子なしファイルすべてに .txt を追加
2-2. for ループの変数修飾子
| 修飾子 | 意味 | 例(%%f = “C:\work\report.”) |
|---|---|---|
%%f |
ファイル名(フルパスまたは相対) | report. |
%%~nf |
拡張子なしのファイル名 | report |
%%~xf |
拡張子のみ(ドット含む) | . (空またはドット) |
%%~nxf |
ファイル名 + 拡張子 | report. |
%%~dpf |
ドライブ + フォルダパス | C:\work\ |
%%~ff |
完全修飾パス | C:\work\report. |
%%~zf |
ファイルサイズ(バイト) | 1024 |
3. 特定の拡張子を指定して追加
追加する拡張子をパラメータ化することで、再利用しやすいスクリプトになります。
@echo off
setlocal enabledelayedexpansion
:: 追加する拡張子を変数で管理(ドットなしで指定)
set "ADD_EXT=csv"
set "COUNT=0"
for %%f in (*.) do (
set "NEWNAME=%%~nf.!ADD_EXT!"
ren "%%f" "!NEWNAME!"
if !ERRORLEVEL! equ 0 (
echo [OK] %%f -> !NEWNAME!
set /a COUNT+=1
)
)
echo 完了: !COUNT! 件処理しました
endlocal
コマンドライン引数で拡張子を指定する
@echo off
setlocal enabledelayedexpansion
:: 使い方: add_ext.bat [追加する拡張子]
:: 例: add_ext.bat txt
:: add_ext.bat csv
if "%~1"=="" (
echo 使い方: %~nx0 拡張子
echo 例: %~nx0 txt
exit /b 1
)
set "ADD_EXT=%~1"
for %%f in (*.) do (
ren "%%f" "%%~nf.!ADD_EXT!"
if !ERRORLEVEL! equ 0 echo [OK] %%f -> %%~nf.!ADD_EXT!
)
endlocal
4. サブフォルダも含めて再帰的に処理(for /r)
サブフォルダ内のファイルも一括処理するには for /r を使います。
@echo off
setlocal enabledelayedexpansion
set "TARGET_DIR=C:\work"
set "ADD_EXT=txt"
set "COUNT=0"
:: for /r: 指定フォルダ以下を再帰的に検索
for /r "%TARGET_DIR%" %%f in (*.) do (
set "DIR=%%~dpf"
set "NAME=%%~nf"
set "NEWNAME=!NAME!.!ADD_EXT!"
:: 既に同名ファイルが存在する場合はスキップ
if exist "!DIR!!NEWNAME!" (
echo [SKIP] 同名ファイル存在: !DIR!!NEWNAME!
) else (
ren "%%~ff" "!NEWNAME!"
if !ERRORLEVEL! equ 0 (
echo [OK] %%~ff -> !NEWNAME!
set /a COUNT+=1
) else (
echo [ERROR] リネーム失敗: %%~ff
)
)
)
echo ===== 完了: !COUNT! 件処理 =====
endlocal
ren コマンドはパスを含むファイル名指定ができません。サブフォルダ内のファイルを処理するには、%%~dpf(フォルダパス)と %%~nf(ファイル名)を分けて取得し、ren "%%~ff" "新ファイル名"(フルパス指定)の形で使います。
サブフォルダ再帰処理の詳細は ファイルの拡張子を一括変換する方法完全ガイド も参照してください。
5. ファイル内容で種別を判定して拡張子を自動付与
ファイルの中身を見て「テキストかバイナリか」「XMLかJSONか」を判別し、適切な拡張子を自動付与するパターンです。
5-1. 先頭行の内容で判定(テキストファイル向け)
@echo off
setlocal enabledelayedexpansion
for %%f in (*.) do (
set "EXT=txt"
set "FIRST="
:: サブルーチンで先頭1行だけ取得(for /f は exit /b で抜ける)
call :get_first "%%f"
:: XML っぽい場合
echo !FIRST! | findstr /i /c:"<?xml" >nul 2>&1
if !ERRORLEVEL! equ 0 set "EXT=xml"
:: JSON っぽい場合
echo !FIRST! | findstr /r /c:"^[{\[]" >nul 2>&1
if !ERRORLEVEL! equ 0 set "EXT=json"
:: CSV っぽい場合(カンマ区切り)
echo !FIRST! | findstr /c:"," >nul 2>&1
if !ERRORLEVEL! equ 0 set "EXT=csv"
ren "%%f" "%%~nf.!EXT!"
echo [OK] %%f -> %%~nf.!EXT!
)
goto :eof
:get_first
:: 先頭1行だけ読んで FIRST にセットし、exit /b で抜ける
for /f "usebackq tokens=* delims=" %%L in ("%~1") do (
set "FIRST=%%L"
exit /b
)
exit /b
:eof_main
endlocal
5-2. PowerShell でマジックバイト(ファイルシグネチャ)判定
@echo off
setlocal enabledelayedexpansion
for %%f in (*.) do (
set "FPATH=%%~ff"
:: PowerShell でファイルの先頭バイトを確認してファイル種別を判定
for /f "usebackq" %%E in (
`powershell -NoProfile -Command "
$bytes = [IO.File]::ReadAllBytes('!FPATH!')[0..3]
$sig = ($bytes | ForEach-Object { $_.ToString('X2') }) -join ' '
switch -Wildcard ($sig) {
'25 50 44 46*' { 'pdf' } # PDF
'89 50 4E 47*' { 'png' } # PNG
'FF D8 FF*' { 'jpg' } # JPEG
'50 4B 03 04' { 'zip' } # ZIP/Office
'D0 CF 11 E0' { 'xls' } # 旧Office
default { 'dat' } # 不明
}"
`) do set "EXT=%%E"
ren "%%f" "%%~nf.!EXT!"
echo [OK] %%f -> %%~nf.!EXT!
)
endlocal
6. ドライラン(実行前に処理対象を確認)
実際にリネームする前に 処理対象と変更後ファイル名を確認する ことで、誤操作を防げます。
@echo off
setlocal enabledelayedexpansion
:: DRY_RUN=1: 確認のみ(実際にはリネームしない)
:: DRY_RUN=0: 実際にリネームする
set "DRY_RUN=1"
set "ADD_EXT=txt"
set "COUNT=0"
if %DRY_RUN% equ 1 (
echo ===== ドライラン(確認のみ・実際には変更しません)=====
) else (
echo ===== 実行モード =====
)
for /r "C:\work" %%f in (*.) do (
set "NEWNAME=%%~nf.!ADD_EXT!"
echo %%~ff -> %%~dpf!NEWNAME!
if %DRY_RUN% equ 0 (
ren "%%~ff" "!NEWNAME!"
if !ERRORLEVEL! equ 0 (
set /a COUNT+=1
) else (
echo [ERROR] リネーム失敗
)
) else (
set /a COUNT+=1
)
)
if %DRY_RUN% equ 1 (
echo ===== 処理予定: !COUNT! 件(DRY_RUN=0 にすると実行されます)=====
) else (
echo ===== 完了: !COUNT! 件リネームしました =====
)
endlocal
7. 重複チェックと安全なリネーム
リネーム先に同名ファイルが既に存在する場合、上書き確認なしに失敗します。事前にチェックして、重複する場合は連番を付けるか、スキップするかを選べます。
7-1. 重複時はスキップ
@echo off
setlocal enabledelayedexpansion
set "ADD_EXT=txt"
for %%f in (*.) do (
set "NEWNAME=%%~nf.!ADD_EXT!"
if exist "!NEWNAME!" (
echo [SKIP] 既に存在します: !NEWNAME!
) else (
ren "%%f" "!NEWNAME!"
echo [OK] %%f -> !NEWNAME!
)
)
endlocal
7-2. 重複時は連番を付けてリネーム
@echo off
setlocal enabledelayedexpansion
set "ADD_EXT=txt"
for %%f in (*.) do (
set "BASENAME=%%~nf"
set "NEWNAME=!BASENAME!.!ADD_EXT!"
set "N=1"
:: 既存ファイルと重複する間、連番を増やす
:check_dup
if exist "!NEWNAME!" (
set "NEWNAME=!BASENAME!_!N!.!ADD_EXT!"
set /a N+=1
goto :check_dup
)
ren "%%f" "!NEWNAME!"
echo [OK] %%f -> !NEWNAME!
)
endlocal
8. 落とし穴5選と対策
落とし穴1:for %%f in (*.) が拡張子なしファイルにマッチしない場合がある
:: NG: *. は「ドットで終わるファイル」にマッチするが
:: 一部の環境では「拡張子なし(ドットなし)」ファイルがマッチしないことがある
for %%f in (*.) do echo %%f
:: OK: ドットなしファイルも確実に捕捉する場合は dir /b と for /f の組み合わせ
for /f "delims=" %%f in ('dir /b ^| findstr /v "\."') do (
echo 拡張子なし: %%f
ren "%%f" "%%f.txt"
)
落とし穴2:ren コマンドは変更後ファイル名にパスを含められない
:: NG: 変更後に別フォルダのパスを指定しても動作しない ren "C:\work\report." "C:\backup\report.txt" :: → エラー: 「ファイルまたはフォルダへのパスが見つかりません」 :: OK: ren は同フォルダ内のリネームのみ。変更後のファイル名のみ指定する ren "C:\work\report." "report.txt" :: 別フォルダに移動したい場合は move または copy + del を使う copy "C:\work\report." "C:\backup\report.txt" del "C:\work\report."
落とし穴3:for /r でサブフォルダをリネームしようとして誤動作
:: NG: for /r のループ中に現在処理中のフォルダ内をリネームすると
:: ループの対象が変化して予期しない動作になる
for /r "C:\work" %%f in (*.) do (
ren "%%f" "%%~nf.txt" :: フルパス %%f を直接 ren に渡してはいけない
)
:: OK: %%~ff(フルパス)を使って ren に渡す
for /r "C:\work" %%f in (*.) do (
ren "%%~ff" "%%~nf.txt"
)
落とし穴4:リネーム後のファイルがループで再処理される
:: NG: for %%f in (*.txt) のようなループ中にファイルを追加すると
:: 追加されたファイルがループに含まれる場合がある
:: OK: 処理対象を先にリストアップしてから一括処理
set "TMPFILE=%TEMP%\filelist_%RANDOM%.txt"
dir /b *. > "%TMPFILE%"
for /f "usebackq delims=" %%f in ("%TMPFILE%") do (
ren "%%f" "%%~nf.txt"
)
del "%TMPFILE%"
落とし穴5:ファイル名に特殊文字(スペース・括弧等)が含まれると失敗する
:: NG: ファイル名にスペースがあると引用符なしで失敗する
for %%f in (*.) do ren %%f %%~nf.txt
:: OK: ファイル名を必ず引用符で囲む
for %%f in (*.) do ren "%%f" "%%~nf.txt"
:: スペースを含むファイル名の例: "my report." -> "my report.txt"
for %%f in (*.) do (
echo 処理: "%%f"
ren "%%f" "%%~nf.txt"
)
エラーハンドリングの詳細は ERRORLEVELを使ったエラーハンドリング完全ガイド、条件分岐は バッチファイルで条件分岐する方法完全ガイド を参照してください。
9. 実践例3本
実践例1:指定フォルダの拡張子なしファイルに .log を追加してバックアップコピー
拡張子のないログファイルを整理するパターンです。元ファイルは残したままコピー先に拡張子付きで保存します。
@echo off
setlocal enabledelayedexpansion
set "SRC_DIR=C:\app\rawlogs"
set "DST_DIR=C:\app\logs"
set "ADD_EXT=log"
set "COUNT=0"
if not exist "%DST_DIR%" mkdir "%DST_DIR%"
for /r "%SRC_DIR%" %%f in (*.) do (
set "NEWNAME=%%~nf.!ADD_EXT!"
set "DST_PATH=%DST_DIR%\!NEWNAME!"
if exist "!DST_PATH!" (
echo [SKIP] 既存: !NEWNAME!
) else (
copy "%%~ff" "!DST_PATH!" >nul
if !ERRORLEVEL! equ 0 (
echo [OK] %%~nxf -> !NEWNAME!
set /a COUNT+=1
) else (
echo [ERROR] コピー失敗: %%~ff
)
)
)
echo ===== 完了: !COUNT! 件コピー =====
endlocal
実践例2:日付付きフォルダへ振り分け(拡張子追加 + 日付管理)
拡張子なしファイルに拡張子を付けながら、今日の日付フォルダへ移動するパターンです。
@echo off
setlocal enabledelayedexpansion
set "SRC_DIR=C:\import\incoming"
set "BASE_DIR=C:\import\processed"
set "ADD_EXT=csv"
:: 今日の日付を取得
for /f "tokens=2 delims==." %%D in ('wmic os get LocalDateTime /value ^| findstr LocalDateTime') do set "DT=%%D"
set "TODAY=%DT:~0,8%"
set "DST_DIR=%BASE_DIR%\%TODAY%"
if not exist "%DST_DIR%" mkdir "%DST_DIR%"
set "COUNT=0"
for %%f in ("%SRC_DIR%\*.") do (
set "NEWNAME=%%~nf.!ADD_EXT!"
if exist "%DST_DIR%\!NEWNAME!" (
echo [SKIP] 既存: !NEWNAME!
) else (
move "%%~ff" "%DST_DIR%\!NEWNAME!" >nul
if !ERRORLEVEL! equ 0 (
echo [OK] %%~nxf -> %DST_DIR%\!NEWNAME!
set /a COUNT+=1
) else (
echo [ERROR] 移動失敗: %%~ff
)
)
)
echo ===== 完了: !COUNT! 件処理 -> %DST_DIR% =====
endlocal
日付フォルダの作成は 日付と時間をファイル名に挿入する方法完全ガイド、ファイルの移動は バッチファイルでファイルを移動する方法完全ガイド を参照してください。
実践例3:フォルダ名をプレフィックスとして付けながら拡張子も追加
「フォルダ名_ファイル名.拡張子」の形式にリネームするパターンです。フォルダ別に管理されたファイルを一箇所にまとめる際に便利です。
@echo off
setlocal enabledelayedexpansion
set "ROOT_DIR=C:\work\export"
set "OUT_DIR=C:\work\merged"
set "ADD_EXT=txt"
if not exist "%OUT_DIR%" mkdir "%OUT_DIR%"
set "COUNT=0"
for /r "%ROOT_DIR%" %%f in (*.) do (
:: 直属フォルダ名を取得
set "FULL_DIR=%%~dpf"
for %%d in ("!FULL_DIR:~0,-1!") do set "DIRNAME=%%~nd"
set "NEWNAME=!DIRNAME!_%%~nf.!ADD_EXT!"
if exist "%OUT_DIR%\!NEWNAME!" (
echo [SKIP] 既存: !NEWNAME!
) else (
copy "%%~ff" "%OUT_DIR%\!NEWNAME!" >nul
if !ERRORLEVEL! equ 0 (
echo [OK] %%~ff -> %OUT_DIR%\!NEWNAME!
set /a COUNT+=1
)
)
)
echo ===== 完了: !COUNT! 件処理 =====
endlocal
ファイル名へのプレフィックス追加は ファイル名の先頭にフォルダ名を一括で付ける方法完全ガイド も参照してください。
拡張子の変換(既存の拡張子を別の拡張子に一括変更)は ファイルの拡張子を一括変換する方法完全ガイド を参照してください。
10. まとめ:使い分け早見表
| やりたいこと | コマンド | ポイント |
|---|---|---|
| カレントフォルダのみ・固定拡張子 | for %%f in (*.) do ren "%%f" "%%~nf.txt" |
最もシンプル |
| カレントフォルダのみ・拡張子を変数化 | for %%f in (*.) do ren "%%f" "%%~nf.!EXT!" |
setlocal enabledelayedexpansion 必須 |
| サブフォルダ再帰 | for /r "フォルダ" %%f in (*.) do ren "%%~ff" "%%~nf.txt" |
%%~ff でフルパス指定 |
| 重複スキップ | if exist でチェック後 ren | 上書き防止に必須 |
| ファイル内容で拡張子判定 | findstr または PowerShell でシグネチャ確認 | マジックバイト判定が確実 |
| 本番前確認(ドライラン) | DRY_RUN 変数で ren をスキップ | 大量ファイル処理前に必須 |
setlocal enabledelayedexpansion 完全ガイド も合わせて参照してください。
FAQ
*. は「ドットで終わるファイル」にマッチします。Windows の内部では拡張子なしファイルの表示が filename.(末尾ドット)か filename(ドットなし)のどちらかで異なります。ドットなし形式の場合は dir /b | findstr /v "\." を使って一覧を取得してから処理してください。\/:*?"<>|)が含まれている、③読み取り専用属性が設定されている、の3つです。if exist で重複チェックをしてからリネームしてください。for /r "フォルダパス" %%f in (*.) do ren "%%~ff" "%%~nf.txt" を使います。%%~ff でフルパスを指定することが重要です。%%f だけを ren に渡すとカレントディレクトリのファイルとして処理されます。dir /b *. 2>nul | find /c /v "" でカレントフォルダの拡張子なしファイル数を取得できます。サブフォルダ含める場合は dir /b /s *. 2>nul | find /c /v "" を使います。また、記事内のドライランコード(DRY_RUN=1)で処理対象の一覧を事前確認することを推奨します。for %%f in ("C:\対象フォルダ\*.") do ren "%%f" "%%~nf.txt" のように、for のパターンにフォルダパスを含めます。for /r を使う場合は第1引数にフォルダパスを指定します: for /r "C:\対象フォルダ" %%f in (*.) do ren "%%~ff" "%%~nf.txt"%%~zf でファイルサイズ(バイト)を取得できます。if %%~zf gtr 0 でサイズ 0 のファイルをスキップできます。例: for %%f in (*.) do if %%~zf gtr 0 ren "%%f" "%%~nf.txt"