У меня есть несколько файлов в папке; для каждого файла мне нужно проанализировать его имя в соответствии с определенным форматом.
Имена файлов могут быть примерно такими:
.\dir1\value1-value2-value3-2.45.13.EXT
.\dir1\value1-value2-value4-value5-1.17.04.EXT
Вкратце, формат каждого файла мне нужен:
| value3 | 2.45.13 |
| value4-value5 | 1.17.04 |
Я попробовал следующий код:
@echo off
setlocal enabledelayedexpansion
for %%p in (
.\dir1\*.EXT
) do (
rem This is the full name of the file (relative path + file extension )
set "relationFileName=%%p"
rem This is the full name of the file without its relative path
for %%f in ("!relationFileName!") do set filename=%%~nxf
rem Removing the file extension
set "stripped_file=!filename:~0,-4!"
rem Getting the length of the filename - function is located below
call :strlen filename_length stripped_file
rem echo file=!stripped_file!(!filename_length!^)
set "delimiter=-"
rem Iterate over the string from the end to the beginning
for /l %%i in (0, 1, !filename_length!) do (
set "index=%%i"
set char=!stripped_file:~%%i,1!
rem echo !stripped_file![!index!] = !char!
if "!char!"= = "!delimiter!" set lastIndex=!index!
)
set /a nextIndex=lastIndex + 1
rem reducing the prefix of `value1-value2-` which we don't want to print out.
set "prefixLength=14"
set /a packageNameLength = lastIndex - prefixLength
rem breaking the filename where the last hyphen character was found
set packageName=!stripped_file:~%prefixLength%,%packageNameLength%!
set packageVersion=!stripped_file:~%nextIndex%!
echo ^| !packageName! ^| !packageVersion! ^|
)
endlocal
REM ********* function *****************************
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
(set^ tmp=!%~2!)
if defined tmp (
set "len=1"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!tmp:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "tmp=!tmp:~%%P!"
)
)
) ELSE (
set len=0
)
)
Результат, который я получаю:
| | value1-value2-value3-2.45.13 |
| | value1-value2-value4-value5-1.17.04 |
Проблема в том, что две строки, в которых хранится позиция, в которой текст должен быть разделен, не оцениваются в команде set (которая разбивает текст).
В коде вы можете видеть, что для prefixLength
, packageNameLength
и nextIndex
их значения не оцениваются в двух командах установки:
set packageName=!stripped_file:~%prefixLength%,%packageNameLength%!
set packageVersion=!stripped_file:~%nextIndex%!
На всякий случай он выводит правильные значения, когда я повторяю эти переменные (prefixLength
, packageNameLength
и nextIndex
).
Это также работает правильно, когда я жестко кодирую значения непосредственно в команде set.
Но когда я помещаю эти переменные в команду set, они не работают.
Кто-нибудь знает, как я могу это исправить?
Я предпочитаю избегать ADFNPSTXZ (в любом случае) в качестве метапеременных (переменных управления циклом).
ADFNPSTXZ также являются модификаторами метапеременных, что может привести к трудно обнаруживаемым ошибкам.
(См. for/f
в приглашении к документации)
@ECHO Off
SETLOCAL enabledelayedexpansion
:: for testing, a set of filenames
SET "filenames=.\dir1\value1-value2-value3-2.45.13.EXT .\dir1\value1-value2-value4-value5-1.17.04.EXT"
FOR %%e IN (%filenames%) DO (
FOR /f "tokens=2,* delims=-" %%b IN ("%%~ne") DO (
rem %%~ne removes the extension & relpath. %%c has the first 2 values removed
SET "remainder=%%c"
SET "remainderelements=!remainder:-= !"
FOR %%o IN (!remainderelements!) DO SET "numpart=%%o"&SET "valuepart=!remainder:-%%o=!"
ECHO %%e --^> !valuepart! + !numpart!
)
)
GOTO :EOF
Учитывая, что %%e
содержит имя файла, например. .\dir1\value1-value2-value3-2.45.13.EXT
,
%%~ne
будет строкой, содержащей просто часть name
строки, т.е. value1-value2-value3-2.45.13
(см. for /?
в подсказке для docco). [исправлены комментарии к процедуре REM]
for /f
затем маркирует эту строку (заключенную в кавычки без подстановочных знаков), используя -
в качестве разделителя, чтобы
токен 1 содержит value1
токен 2 содержит value2
токен * содержит value3-2.45.13
(остаток строки)
Поскольку токены 2
и *
выбраны, токен 2
присваивается %%b
(выбранной метапеременной), а токен *
— %%c
(следующая алфавитная переменная).
Затем remainder
присваивается value3-2.45.13
, поскольку %%c
не может быть подстрокой.
remainderelements
затем присваивается value3 2.45.13
путем замены каждого -
на пробел. (см. set /? из подсказки для docco)
%%o
затем поочередно устанавливается на value3
и 2.45.13
, поскольку remainderelements
содержит простой список, разделенный пробелами.
numpart
устанавливается для каждого элемента в этом списке по очереди, поэтому он остается установленным для последнего элемента 2.45.13
, когда цикл заканчивается.
valuepart
аналогично устанавливается значение remainder
(value3-2.45.13
) с -
+ последним элементом, 2.45.13
заменяется ничем на последней итерации цикла for %%o
.
Бинго!
Ох! будь немного осторожнее! %%c
и %%C
— это две разные вещи — единственный случай, когда синтаксис пакетного языка различается из-за регистра (о котором я знаю)
Другой подход:
@echo off
setlocal EnableDelayedExpansion
rem Create a file list for testing
set "filenames=.\dir1\value1-value2-value3-2.45.13.EXT .\dir1\value1-value2-value4-value5-1.17.04.EXT"
rem Process all filenames: call :split subroutine with file name only
for %%f in (%filenames%) do call :split "%%~Nf"
goto :EOF
rem Get from third to last-but-one hypen-separated elements ("head") and last element
:split
set "list=%~1" & set "head = " & set "i=0"
set "last=%list:- = " & set /A i+=1 & (if !i! geq 3 set "head=!head!-!last!") & set "last=%"
echo ^| %head:~1% ^| %last% ^|
В этом методе эффективно используется двойная обработка, которая происходит, когда список элементов %заменяется% выражением, которое включает расширение !delayed! Самый простой способ понять, что происходит, — это поместить команду ECHO ON
в подпрограмму :split
и внимательно просмотреть исполняемый код...
Дополнительное объяснение дано в комментариях ниже этот ответ, а первоисточник этого метода приведен в ссылке в таком ответе.
Ух ты... Я удивлен, увидев такой короткий ответ, который тоже работает :). Можете ли вы объяснить мне немного об этом? Например, как получается, что из %%C удалены относительный путь и первые два значения?