フォルダ情報を書き出すバッチ

指定した対象フォルダ内のサブフォルダ名やファイル名を、テキストファイルに書き出すためのバッチです。

※2018/08/12 完全リニューアルしました。


目次

  1. バッチ本体
  2. 実行時の動作
  3. 解説
  4. 超単純化バッチ

1. バッチ本体

まず初めにバッチを表示しておきます。4行目にある【設定ここから】の下部で、フォルダパスを2つ指定して使います。

  • set Target … 情報を取得する対象のフォルダパス
  • set Output … テキストファイルを出力するフォルダパス
@echo off
cd /d %~dp0

rem ----------【設定ここから】----------
rem Targetに対象元フォルダパスを指定してください。相対パスでも指定できます。未指定の場合、当フォルダになります。
set Target=

rem Outputに出力先フォルダパスを指定してください。相対パスでも指定できます。未指定の場合、当フォルダになります。
set Output=

rem パスは「""」で囲まなくても指定できます。末尾の「\」の有無も問いません。
rem ----------【設定ここまで】----------

rem Targetのチェック
if "%Target%" == "" (
	set Target=%~dp0
	goto OutputCheck
)

set Target=%Target:"=%
set Target=%Target:/=\%

echo %Target% | find ":" > nul
if %ERRORLEVEL% neq 0 (
	set Target=%~dp0%Target%
)

set Target=%Target%\
set Target=%Target:\\=\%

set ExistTemp=%Target:\\=:%
if not exist "%ExistTemp%" (
	echo. & echo 対象元フォルダ "%Target%" は存在しません。処理を中止します。
	pause > nul & exit /b
)

rem Outputのチェック
:OutputCheck
if "%Output%" == "" (
	set Output=%~dp0
	goto SetName
)

set Output=%Output:"=%
set Output=%Output:/=\%

echo %Output% | find ":" > nul
if %ERRORLEVEL% neq 0 (
	set Output=%~dp0%Output%
)

set Output=%Output%\
set Output=%Output:\\=\%

set ExistTemp=%Output:\\=:%
if not exist "%ExistTemp%" (
	echo. & echo 出力先フォルダ "%Output%" は存在しません。処理を中止します。
	pause > nul & exit /b
)

rem 出力ファイルに付加する名前を決定
:SetName
set Name=

set Target=%Target:~0,-1%
if "%Target%\" == "%~dp0" (goto Run)

rem Targetに「\」があればエラー0でスルー、なければドライブレター取得
echo %Target% | find "\" > nul
if %ERRORLEVEL% neq 0 (
	set Name=(%Target:~0,1%^)
	goto Run
)

rem Targetの空白対策に""で囲み、後ろから255文字まで「\」を探す
set Target="%Target%"

setlocal enabledelayedexpansion
set /a i=-3

:NameLoop
if %i% lss -255 (goto Run)
set Check=!Target:~%i%,1!
if not "%Check%" == "\" (
	set /a i=%i% - 1
	goto NameLoop
)

rem 「\」より後ろのフォルダ名を取得
set Target=%Target:"=%
set /a i=%i% + 2
set Name=(!Target:~%i%!)

rem 出力開始
:Run
echo 対象元フォルダ : %Target% & echo 出力先フォルダ : %Output% & echo.
echo フォルダ情報を出力します。必要に応じてオプションを指定してください。
echo キャンセルする場合は、オプション以外の文字を指定してください。 & echo.
echo   n : サブフォルダ名とファイル名のリスト(規定値)
echo   p : サブフォルダ内も含めた全ファイルの絶対パスのリスト
echo   t : サブフォルダ内も含めた全ファイルのツリー表示 & echo.

set /p AnswerA=オプション指定 (n/p/t) : 
if /i "%AnswerA%" == "" (goto OptN)
if /i "%AnswerA%" == "n" (goto OptN)
if /i "%AnswerA%" == "p" (goto OptP)
if /i "%AnswerA%" == "t" (goto OptT)

echo. & echo 処理を中止します。
pause > nul & exit /b

:OptN
set DesFile=FolderList_Name%Name%.txt
dir /b /o:gn "%Target%\">"%Output%%DesFile%"
goto Show

:OptP
set DesFile=FolderList_Path%Name%.txt
dir /a:-d /b /o:n /s "%Target%\">"%Output%%DesFile%"
goto Show

:OptT
set DesFile=FolderTree%Name%.txt
rem ドライブの場合は「\」あり、フォルダの場合はなしで実行
echo %Target% | find "\" > nul
if %ERRORLEVEL% neq 0 (
	tree /f "%Target%\">"%Output%%DesFile%"
) else (
	tree /f "%Target%">"%Output%%DesFile%"
)

:Show
echo. & echo "%Output%%DesFile%" を出力しました。

set /p AnswerB=このファイルを開きますか? (y/n) : 
if /i "%AnswerB%" == "" (goto Open)
if /i "%AnswerB%" == "y" (goto Open)
if /i "%AnswerB%" == "yes" (goto Open)

echo. & echo 処理が完了しました。何かキーを押すと終了します。
pause > nul & exit /b

:Open
start "" "%Output%%DesFile%"
exit /b

2. 実行時の動作

このバッチの目的は、「Target」に指定されたフォルダの情報を「Output」フォルダにテキストファイルで出力することです。

フォルダパスの指定は、絶対パスでも相対パスでも動作します。パスに空白が含まれていても「””」で囲む必要はありません(もちろん囲んでも構いません)。また、パスの末尾に「\」を付けるかどうかも問いません。もしパスを指定せず空欄にした場合は、このバッチが置かれているフォルダが対象になります。

FolderInfo.bat を実行すると、フォルダ情報を取得するオプションを選択できます。

  • n … サブフォルダ名とファイル名のリスト(無指定の場合の規定値)
  • p … サブフォルダ内も含めた全ファイルの絶対パスのリスト
  • t … サブフォルダ内も含めた全ファイルのツリー表示

オプションにしたがって「Target」フォルダの情報が入ったテキストファイルが「Output」に作成されます。直後にそのテキストファイルを開くかどうかの選択肢が出ます。

開かない場合はそのまま終了します。

出力されるテキストファイルの名前は、各オプションによって変わります。

  • n … FolderList_Name(Targetフォルダ名).txt
  • p … FolderList_Path(Targetフォルダ名).txt
  • t … FolderTree(Targetフォルダ名).txt

「Target」を空欄にしてこのバッチのフォルダを対象にした場合、括弧書きは付きません。


3. 解説

3-1. Target と Output のフォルダパスをチェックする

まずは指定された2つのパスが正しいかどうかチェックしています。

set Target=
set Output=

この2ヶ所に入力されたパスは、それぞれの変数に文字列として格納されています。空白も「”」も1つの文字です。
この文字列から 絶対パス/相対パス/ドライブ の区別をして整形し、フォルダパスとして使用します。

if "%Target%" == "" (
	set Target=%~dp0
	goto OutputCheck
)

パスが未指定の場合には、このバッチが置かれているフォルダ「%~dp0」を指定するようにします。
この場合は整形する必要がないので、gotoで「Output」をチェックするところまで飛びます。

set Target=%Target:"=%
set Target=%Target:/=\%

ここでは全ての「”」を除去し、「/」を「\」に変換しています。
%変数:置換前文字列=置換後文字列%」という記述で文字列を置換できるので、それを使っています。
「/」はパスの区切りとして使えなくはないのですが、一部のコマンドが動作しないので「\」に変換した方が良いでしょう。

ちなみに「Target」が未指定のときに置換をかけてしまうとエラーとなり、強制終了します。
今回は先にチェックしてあるので大丈夫ですが、もし未指定の変数に置換をかける可能性がある場合は、先に「””」で囲んでおけばとりあえず強制終了にはなりません。
それでも中身のない文字列に置換をかけると変な値になるので、変数が空でないかどうか、事前にチェックした方が良いと思います。

echo %Target% | find ":" > nul
if %ERRORLEVEL% neq 0 (
	set Target=%~dp0%Target%
)

入力されたパスが相対パスかどうか判定しています。
「echo %Target%」をパイプ「|」で「find “:”」に渡すことにより、「Target」変数に「:」が含まれていればエラーレベル0になります。「:」があってエラーレベル0の場合を絶対パスまたはドライブ指定、そうでない場合を相対パス指定と判断します。
エラーレベルが0でない、つまり相対パスの場合はif分岐に入り、自身のフォルダパスを指す「%~dp0」を前に付けて、絶対パスにしています。

set Target=%Target%\
set Target=%Target:\\=\%

set ExistTemp=%Target:\\=:%
if not exist "%ExistTemp%" (
	echo. & echo 対象元フォルダ "%Target%" は存在しません。処理を中止します。
	pause > nul & exit /b
)

ここでは「\」の過不足を処理してからフォルダの存在確認をしています。
まず末尾に1つ「\」を追加し、次に連続した「\\」だけ「\」に変換します。こうすることで、入力されたパスの末尾に「\」があってもなくても、必ず1つの「\」で終わるようになります。

実は同時に、もし相対パス指定として「set Target=\Sub\」と「\」で囲んで入力されていても大丈夫なようになっています。
相対パスの前に付加する「%~dp0」は末尾に「\」が付いているので、絶対パスにすると「親フォルダ\\Sub\」のように「\\」ができてしまいます。このパターンも「\」が1つだけの状態に整形されます。

その次に「ExistTemp」という変数に「Target」を入れて、フォルダの存在確認をします。
このとき、「\」を「:」に変換してから行っているのは、入力されたパスが誤って「Sub\\」となっているような場合に対応するためです。
なぜが「if exist」によるフォルダの存在確認では、いくつ「\」が連続していても1つとして判断され、エラーになりません。
「\\」があるままチェックを通過させると後のコマンドではエラーになってしまいますので、フォルダ名に使えない「:」に変換してから存在確認を行っています。フォルダが存在しなければメッセージを表示して終了します。

「Output」のチェックも原則として同様です。
ここまでだいぶ長くなりましたが、入力されたフォルダパスの整形と存在確認ができました。

3-2. 出力ファイルに付加する名前を決める(:SetName)

今回は、出力ファイル名に「Target」フォルダの名前を入れてみました。
もし必要なければごっそり削除して構いませんが、変数の文字列の切り出しを行っているので、方法論としては参考になる部分があるかもしれません。

:SetName
set Name=

set Target=%Target:~0,-1%
if "%Target%\" == "%~dp0" (goto Run)

付ける名前は「Name」という変数に格納することにしました。
「%Target:~0,-1%」は、最後の1文字「\」を削除する記述です。後のテキストファイル書き出しのとき、特に「tree」コマンドに対応するために削除しています。
文字列の切り出しは「%変数:~開始位置,文字数%」となっています。文字数が-1だと、末尾1文字が削除されます。
この場所に書いたのは、「Target」が未指定で自動入力したときと、入力されたパスを整形したときの両方で通過する行に書きたかったからです。

「Target」が自身のフォルダのときは出力ファイル名に括弧書きを付けない仕様のため、ifで拾って「goto Run」で飛ばしています。

echo %Target% | find "\" > nul
if %ERRORLEVEL% neq 0 (
	set Name=(%Target:~0,1%^)
	goto Run
)

ここでは、絶対パスにした「Target」がドライブ指定かフォルダ指定かを判定しています。
「Target」の文字列に「\」があればエラーレベル0、なければ1です。末尾の「\」を削除してあるため、ドライブ指定の場合は「C:」のように「\」がなく、エラーレベルが1になります。
ドライブ指定の場合は最初の1文字を取れば良いので、「%Target:~0,1%」で開始位置0(1文字目)から文字数1を取得しています。

if分岐の中で閉じ括弧「)」を使うときは、ifの閉じ括弧と混同されないようにエスケープ記号を付けて「^)」と記述する必要があります。

set Target="%Target%"

setlocal enabledelayedexpansion
set /a i=-3

:NameLoop
if %i% lss -255 (goto Run)
set Check=!Target:~%i%,1!
if not "%Check%" == "\" (
	set /a i=%i% - 1
	goto NameLoop
)

ここからが一番ややこしいです。やりたいことは、出力ファイルの括弧書きに入れる「Target」のフォルダ名を取得することです。
そのためにまず、パスの文字列の中で最後の「\」が何文字目にあるのかを判定します。最後の「\」より後ろの文字列が、取得したいフォルダ名になります。

最初に「Target」を「””」で囲むのは、フォルダ名に空白が含まれていたときに、この後の処理でエラーとなるのを防ぐためです。

「setlocal enabledelayedexpansion」は、遅延環境変数を使えるようにするための記述です。ここでは「ループ処理の前に書いておくもの」くらいの気持ちで見ておきましょう。
「setlocal」は「endlocal」とセットですが、バッチの終了時に「endlocal」が暗示的に実行されるので、通常は書かなくて大丈夫です。

「:NameLoop」から「goto NameLoop」までがループ処理です。
ループカウントは「i」に入れることとし、初期値は「-3」です。この「i」は「Target」パスの後ろから1文字ずつチェックするカウンターになります。
フォルダ名の長さは最長255文字らしいので、最後の「”」を入れて後ろ256文字分をチェックすることを考えます。
このとき-2文字目(フォルダ名の最後)が「\」の可能性はないので、-3文字目からチェックをスタートします。また、-256文字目(フォルダ名の最初)が「\」である可能性もないので、-255文字までチェックすれば良いことになります。
というわけで-3文字目から順に前をチェックしていき、-255文字目まで「\」を探します。

「if %i% lss -255」で、「i」が-255より小さいときはループを抜けます。
実際にはその前に「\」が見つかるはずなのでこの分岐に入ることはありませんが、ループ処理を行うときは、予期しない無限ループを防ぐために数値でカウンターリミットを設けておいた方が安心です。

「Check」が判定文字です。「!Target:~%i%,1!」で「Target」の「i」文字目から1文字を取得します。
「!」で変数を囲んでいるのが遅延環境変数です。簡単に言えば「その行にきたときに計算し直される」ようにする方法です。
「set」コマンドを使用する際にこれを「%」で囲んでいると、「Check」に「”Targetパスi」のような意図しない文字列が入り、無限ループ化してしまいます。

あとは「Check」が「\」であるかどうか判定し、違う場合は「i」を-1して「:NameLoop」に戻ってループします。

set Target=%Target:"=%
set /a i=%i% + 2
set Name=(!Target:~%i%!)

ループを抜けたら、文字列判定のために追加した「”」をまず除去します。
「i」には「\」が何文字目にあるかが入っていますので、「”」の1文字分を抜き、さらに「\」の次の文字を取るために1文字ずらしますので+2しています。

「!Target:~%i%!」で、「Target」の「i」文字目から最後までの文字列となります。ここでもループ内で変化させた「i」を使って「set」するので、遅延環境変数で指定しています。

3-3. オプション選択後にテキストファイルを出力する(:Run)

set /p AnswerA=オプション指定 (n/p/t) : 
if /i "%AnswerA%" == "" (goto OptN)
if /i "%AnswerA%" == "n" (goto OptN)
if /i "%AnswerA%" == "p" (goto OptP)
if /i "%AnswerA%" == "t" (goto OptT)

やっと終盤です。
「set /p ~ :」で、ユーザーからの入力を待って変数に入れることができます。その後のifでパターン分けをして、それぞれの行へ「goto」で飛びます。
ifに「/i」を付けておくと、入力された文字の大文字と小文字を区別しないで判定してくれます。

3-4. サブフォルダ名とファイル名のリスト(:OptN)

set DesFile=FolderList_Name%Name%.txt
dir /b /o:gn "%Target%\">"%Output%%DesFile%"
goto Show

まず変数「DesFile」に出力ファイル名を入れます。「%Name%」が括弧書きの付加名です。

次に「dir」コマンドを使って「Target」フォルダの情報を取得し、それを「>」の後の出力ファイルに書き出しています。
「dir」には様々なオプションがあり、次のように取得する情報を指定できます(抜粋)。

/a:dディレクトリ
r読み取り専用
h隠しファイル
aアーカイブ
sシステム ファイル
i非インデックス対象ファイル
l再解析ポイント
その属性以外
/bファイル名のみを表示
/o:n名前順 (アルファベット)
sサイズ順 (小さい方から)
e拡張子順 (アルファベット)
d日時順 (古い方から)
gグループ (ディレクトリから)
降順
/sサブディレクトリを含めたすべてのファイルを表示

「/b /o:gn」を指定すると、フォルダを先頭にした名前順で、フォルダ名とファイル名のみのリストが作られます。

もしこれをファイル名のみにしたい場合は、「dir」コマンドを次のように書き換えてください。

dir /a:-d /b /o:n "%Target%\">"%Output%%DesFile%"

「-d」のように「-」を付けることで、その属性を対象外にすることができます。

3-5. サブフォルダ内も含めた全ファイルの絶対パスのリスト(:OptP)

dir /a:-d /b /o:n /s "%Target%\">"%Output%%DesFile%"

絶対パスのリストも「dir」を使います。
「/s」オプションが全フォルダと全ファイルの絶対パスを表示しますので、「/a:-d」でフォルダを除いています。

3-6. サブフォルダ内も含めた全ファイルのツリー表示(:OptT)

echo %Target% | find "\" > nul
if %ERRORLEVEL% neq 0 (
	tree /f "%Target%\">"%Output%%DesFile%"
) else (
	tree /f "%Target%">"%Output%%DesFile%"
)

「tree」コマンドでツリー形式を表示でき、「/f」を付けるとフォルダ内の全ファイルが対象になります。

このコマンドは鬼門です。ポイントとなるのは「Target」がドライブ指定かフォルダ指定かで記述が異なる点です。
ドライブ指定の場合は最後に「\」を付けて実行、フォルダ指定の場合は最後に「\」を付けないで実行する必要があります。ここの融通は利きません。

そのため、「find」を使って「Target」に「\」が入っていないときをドライブ指定として判定し、「\」を付加して実行しています。
フォルダ指定の場合は「Target」をそのまま使って実行しています。

この部分の動作のために、フォルダパスを整形した際に最後の「\」を付けないようにしました。除去するより付加するほうが簡単なので。

「tree」コマンドには並べ替えのオプションがありません。実行結果からみるに、作成順に並ぶようです。

3-7. 出力ファイルの表示(:Show)

set /p AnswerB=このファイルを開きますか? (y/n) : 

で、出力ファイルをその場で開くかどうか聞いています。実は「n」は判定に使っていないので何でもよいのですが、「y/n」で「yes/no」を表すのが常套手段のようです。

start "" "%Output%%DesFile%"

で「start」コマンドを使って出力ファイルを開き、バッチの方は終了します。
ここを「”%Output%%DesFile%”」とだけ書くと、バッチの画面が残ってしまいます。


4. 超単純化バッチ

対象フォルダと出力方式が決まっていてオプション選択の必要がない場合のために、超単純化したバッチも書いておきます。パスのチェックをしないため、書き間違えると動作しませんのでご注意ください。

4-1. サブフォルダ名とファイル名のリスト

cd /d %~dp0
dir /b /o:gn "対象フォルダパス">"出力ファイルパス"
start "" "出力ファイルパス"

4-2. サブフォルダ内も含めた全ファイルの絶対パスのリスト

cd /d %~dp0
dir /a:-d /b /o:n /s "対象フォルダパス">"出力ファイルパス"
start "" "出力ファイルパス"

4-3. サブフォルダ内も含めた全ファイルのツリー表示

cd /d %~dp0
tree /f "対象ドライブ文字:\">"出力ファイルパス"
start "" "出力ファイルパス"

または

cd /d %~dp0
tree /f "対象フォルダパス">"出力ファイルパス"
start "" "出力ファイルパス"

実は最初に挙げた長いバッチは、パスを両方とも指定せずに、取得したいフォルダにバッチを投げ込んで実行する用途で作成しました。
未指定パスを自動で補完したり、情報取得オプションを選択できるようにしたのはそのためです。

使用環境が完全に決まっている場合は超単純化バッチを使ってください。自分はパスを入力するのが面倒なので、専ら長いバッチの方を使っています。

Leave a comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

15 + 2 =