Эта задача создания каталога и перемещения файла может быть выполнена с помощью следующего кода пакета:
@echo off
if "%~1" == "" (pushd "%~dp0") else (
pushd "%~1"
if errorlevel 1 (
echo ERROR: Directory "%~1" does not exist.
echo/
pause
exit
)
)
setlocal EnableExtensions DisableDelayedExpansion
set "FolderName=\"
for /F "eol=| delims=" %%I in ('dir /A-D /B /O-N 2^>nul') do if not "%%~fI" == "%~f0" (
for /F "eol=| tokens=1 delims=[]" %%J in ("%%~nI") do (
set "FileName=%%J"
set "FullName=%%I"
setlocal EnableDelayedExpansion
set "DiskAddon=!FileName:*(Disk =(Disk !"
if not "!DiskAddon!" == "!FileName!" for /F "delims=" %%V in ("!DiskAddon!") do set "FileName=!FileName:%%V=!"
for /F "eol=| delims=" %%K in ("!FolderName!") do (
if "!FileName:%%K=!" == "!FileName!" (
md "!FileName!" 2>nul
move /Y "!FullName!" "!FileName!\"
for /F "eol=| delims=" %%V in ("!FileName!") do (
endlocal
set "FolderName=%%V"
)
) else (
move /Y "!FullName!" "!FolderName!\"
endlocal
)
)
)
)
endlocal
popd
Файл пакета может быть сохранен в каталоге с файлами для обработки или вызывается с путем к каталогу, к которому обработать. Каталог пакетных файлов или указанный каталог временно становится текущим каталогом.
Переменная среды FolderName
хранит имя последней созданной папки в зависимости от имени файла. Переменная среды FolderName
определена с недопустимым именем \
, чтобы всегда работать в true ветви самого внутреннего IF условия в первом файле.
Первый FOR выполняется в фоновом режиме еще один командный процесс, запущенный с %ComSpec% /c
, и командная строка между '
добавляется в качестве дополнительных аргументов. Выполняется так: Windows установлен в C:\Windows
:
C:\Windows\System32\cmd.exe /c dir /A-D /B /O-N 2>nul
Команда DIR , выполняемая запущенным фоновым процессом обработки команды, обрабатывает STDOUT (стандартный вывод ) фонового командного процесса
- только имена файлов из-за опции
/A-D
(атрибут не каталог) - в голом формате, что означает просто имя файла с расширением файла, но без путь к файлу из-за опции
/B
- в обратном порядке по имени из-за
/O-N
- всех файлов в текущем каталоге, соответствующих шаблону подстановочных знаков по умолчанию
*
.
Здесь необходим обратный порядок, чтобы сначала выводить более короткие имена файлов, соответственно те, которые выводятся с [...]
, а затем с (Disk x of y)
.
Возможно, что файл вообще не найден. В этом случае сообщение об ошибке будет выдано DIR для обработки STDERR (стандартная ошибка). Это не важное сообщение об ошибке подавляется путем перенаправления его на устройство NUL . FOR l oop ничего не делает для файлов, найденных в указанном каталоге.
Прочтите статью Microsoft о Использование операторов перенаправления команд для объяснения 2>nul
, Оператор перенаправления >
должен быть экранирован с помощью символа вставки ^
в FOR командной строке, чтобы интерпретироваться как литеральный символ, когда интерпретатор команд Windows обрабатывает эту командную строку перед выполнением команды FOR который выполняет встроенную dir
командную строку в отдельном командном процессе, запущенном в фоновом режиме.
FOR с используемой опцией /F
и командной строкой, заключенной в '
, захватывает все вывод для обработки STDOUT запущенного фонового командного процесса и построчно обрабатывает этот вывод после запуска cmd.exe
после завершения выполнения командной строки.
Пустые строки по умолчанию игнорируются FOR , которые здесь не встречаются.
FOR будет разбивать каждую строку на подстроки по умолчанию с использованием обычного пробела и горизонтальной табуляции в качестве разделителя строк и будет назначать только первая разделенная пробелами / табуляцией подстрока для указанной переменной l oop I
. Такое поведение разделения строк здесь нежелательно, поскольку имена файлов могут содержать один или несколько пробелов. По этой причине пустой список разделителей определяется с опцией delims=
, чтобы полностью отключить поведение разделения строк.
FOR будет игнорировать также строки, на которых первая подстрока после разделения строки является точка с запятой, которая является символом конца строки по умолчанию. Имя файла может начинаться с точки с запятой. Поэтому eol=|
переопределяет символ конца строки на вертикальную черту, которую ни одно имя файла не может содержать в своем имени.
Таким образом, каждое имя файла с расширением файла полностью присваивается переменной l oop один за другим I
для дальнейшей обработки.
Пакетный файл, обрабатываемый в данный момент cmd.exe
, игнорируется при обработке имен файлов из-за условия IF в первой командной строке FOR .
Следующая FOR обрабатывает только имя файла без расширения файла как строку. Опции eol=| tokens=1 delims=[]
предназначены для разбиения имени файла на подстроки с использованием квадратных скобок в качестве разделителей с первой подстрокой, назначенной указанной переменной l oop J
, и без игнорирования имен файлов, начинающихся с ;
. Это необходимо для получения имени файла, например 3D Pool (1989)(Firebird Software)[cr Steel McKraken - Exocet].dsk
, просто 3D Pool (1989)(Firebird Software)
, назначенного для l oop переменная J
для дальнейшей обработки.
Переменные не работают так, как ожидается описывает что командный процессор Windows заменяет все %variable%
в командном блоке, начиная с (
и заканчивая сопоставлением )
по текущему значению переменной среды, прежде чем команда будет выполнена с использованием командного блока. Это означает, что требуется отложенное расширение , которое нельзя включить выше FOR l oop, поскольку в противном случае каждый !
в имени файла будет интерпретироваться также как начало / конец среды ссылка на переменную расширена с задержкой на выполнение. По этой причине отложенное расширение включено и отключено внутри l oop.
. Прочитайте этот ответ , чтобы узнать подробнее о командах SETLOCAL и ENDLOCAL .
Переменная среды DiskAddon
определяется либо с частью (Disk x of y)
в конце имени файла, если строка (Disk
находится где-то в имени файла, либо с неизмененным именем файла, если не содержит (Disk
в all.
Таким образом, если строка, присвоенная DiskAddon
, отличается от строки имени файла, строка (Disk x of y)
также удаляется из имени файла с использованием подстановки с расширенной строкой с задержкой. Текущая строка, назначенная для DiskAddon
, должна быть временно назначена для переменной al oop, чтобы иметь возможность использовать эту строку в отложенной подстановке расширенной строки. Невозможно указать задержанную ссылку на расширенную переменную среды внутри отложенной замены расширенной переменной среды.
Имя текущей папки должно быть временно назначено также переменной oop, чтобы иметь возможность использовать эту строку в отложенная замена расширенной строки при следующем IF условии. Таким образом, следующий FOR предназначен только для назначения имени текущей папки, назначенного переменной окружения FolderName
переменной l oop K
.
IF условие - это сравнение текущего (усеченного) имени файла с учетом регистра со всеми вхождениями текущего имени папки, без учета регистра с текущим (усеченным) именем файла. Другими словами, условие IF проверяет, не содержит ли текущее (усеченное) имя файла имя текущей папки. В этом случае текущий файл необходимо переместить в новую папку.
Поэтому папка создается с текущим (усеченным) именем файла с подавлением сообщения об ошибке с перенаправлением на устройство NUL на уже существующей папке и перемещая текущий файл в эту папку. Далее необходимо отключить отложенное расширение и восстановить предыдущую среду. Но необходимо передать имя папки только что созданной папки в предыдущую среду. Итак, еще раз FOR l oop используется с именем текущей папки или усеченным именем файла, назначенным для l oop variable V
, чтобы получить имя папки, назначенное переменной среды FolderName
в предыдущем environment.
В противном случае текущий файл перемещается в ту же папку, что и предыдущий файл, перед отключением отложенного расширения и восстановлением предыдущей среды.
Наконец, пакетный файл восстанавливает исходную среду, а также исходный текущий каталог.
Здесь также есть альтернативное решение, использующее подпрограмму, которая упрощает подстановку строк, поскольку в командном блоке нет командной строки.
@echo off
if "%~1" == "" (pushd "%~dp0") else (
pushd "%~1"
if errorlevel 1 (
echo ERROR: Directory "%~1" does not exist.
echo/
pause
exit
)
)
setlocal EnableExtensions DisableDelayedExpansion
set "FolderName=\"
for /F "eol=| delims=" %%I in ('dir /A-D /B /O-N 2^>nul') do if not "%%~fI" == "%~f0" call :ProcessFile "%%I"
goto EndBatch
:ProcessFile
for /F "eol=| tokens=1 delims=[]" %%J in ("%~n1") do set "FileName=%%J"
setlocal EnableDelayedExpansion
set "DiskAddon=!FileName:*(Disk =(Disk !"
if not "!DiskAddon!" == "!FileName!" set "FileName=!FileName:%DiskAddon%=!"
if not "!FileName:%FolderName%=!" == "!FileName!" endlocal & goto MoveFile
endlocal & set "FolderName=%FileName%"
md "%FolderName%" 2>nul
:MoveFile
move /Y %1 "%FolderName%\"
goto :EOF
:EndBatch
endlocal
popd
См. одну строку с несколькими командами, использующими Windows пакетный файл для объяснения оператора &
, использованным в этом пакетном файле два раза, чтобы избежать использования командного блока.
Чтобы понять используемые команды и то, как они работают, откройте окно командной строки , выполните там следующие команды и полностью прочитайте все страницы справки, отображаемые для каждой команды.
call /?
... объясняет %~dp0
, %~f0
, %1
, %~1
, %~n1
dir /?
echo /?
endlocal /?
exit /?
for /?
goto /?
if /?
md /?
move /?
pause /?
popd /?
pushd /?
set /?
setlocal /?
PS: использование сценария PowerShell для всей задачи будет намного лучше. PowerShell поддерживает встроенные строковые функции, такие как поиск строки в строке или изменение строки с помощью регулярного выражения для получения базового имени файла. Командный процессор Windows, выполняющий пакетный файл, предназначен для выполнения команд и исполняемых файлов, а не для работы со строками, необходимыми для этой задачи.