Как развернуть две локальные переменные внутри цикла for в командном файле
Я пытаюсь написать скрипт, который должен читать из списка файлов и копировать каждый элемент в новое место, следуя той же структуре.
Первоначально это было предназначено для копирования файлов, которые были изменены в пределах диапазона коммитов Git, но, поскольку я нашел более простой способ (сначала "сценарий" на C#, а затем этот), сейчас я делаю это только в интересах изучения.
Это пример file_list.txt:
config.php
repository\file picker.php
user\edit.php
Это мой файл .bat:
@echo off
setlocal enabledelayedexpansion
set "source=input dir"
set "target=output dir"
for /f "tokens=* usebackq" %%A in ("file_list.txt") do (
set "FILE=%%A"
set "dest_file_full=%target%\!FILE:%source%=!"
set "dest_file_filename=%%~nxA"
::set dest_file_dir=(!dest_file_full - !dest_file_filename)
if not exist "!dest_file_dir!" (
md "!dest_file_dir!"
)
set "source_file_full=%source%\!FILE:%source%=!"
copy "!source_file_full!" "!dest_file_dir!"
)
pause
Обратите внимание на закомментированную строку set dest_file_dir=(!dest_file_full - !dest_file_filename)
, Это псевдокод. Мне нужно использовать подстановку переменных здесь, но cmd.exe не расширит две локальные переменные за один шаг.
Я уже пробовал трубу...
echo set dest_file_dir=^!dest_file_full:!dest_file_filename!=^! | cmd
или используя call
:
call set dest_file_dir=!!dest_file_full:!dest_file_filename!=!!
Но ни один не будет работать.
Я делаю что-то не так в этом, или есть элегантный способ (следовательно, без использования временного файла), чтобы сделать это?
Благодарю.
3 ответа
Этот ответ раскрывает только расширение "вложенных переменных":
Короче говоря, используйте это:
call set "dest_file_dir=%%dest_file_full:!dest_file_filename!=%%"
Спасаясь ^!
или используя call
а также !!
не работает для расширения вложенных переменных с отложенным расширением.
Трубы не могут помочь, так как они инициализируют новый cmd
экземпляры для обеих сторон, которые используют свои собственные отдельные среды.
Давайте сначала посмотрим на вложенные переменные, используя немедленное расширение:
call set "dest_file_dir=%%dest_file_full:%dest_file_filename%=%%"
call
вводит второй этап синтаксического анализа, который требуется для разрешения вложенности. call
Команда получает частично расширенную командную строку, как это (предположим, %dest_file_filename%
расширяется до config.php
, например):
set "dest_file_dir=%dest_file_full:config.php=%"
Это не что иное, как обычный синтаксис замены подстроки с немедленным расширением, которое расширяется во время упомянутой второй фазы синтаксического анализа.
Примечание: этот подход не работает в случае, если значение dest_file_filename
начинается с %
, ~
, или же *
(но в любом случае это запрещенный символ для имен файлов), или если он содержит =
! Кроме того, если присутствует, "
может также вызвать проблемы (но такие также не допускаются в именах файлов)!
Теперь давайте попробуем аналогичный подход, но с отложенным расширением:
Согласно сообщению Как Windows Command Interpreter (CMD.EXE) анализирует сценарии?, call
вводит вторую фазу синтаксического анализа, но это не включает отложенное расширение, а только немедленное расширение; так что отложенное расширение выполняется только во время первой фазы синтаксического анализа.
Подобно приведенному выше решению, использующему немедленное раскрытие, мы должны убедиться, что строки поиска и замены в синтаксисе замены подстроки раскрыты во время первой фазы синтаксического анализа, и что общее выражение обрабатывается вторым; так что давайте использовать это:
call set "dest_file_dir=%%dest_file_full:!dest_file_filename!=%%"
Итак call
Команда получает следующую частично расширенную командную строку:
set "dest_file_dir=%dest_file_full:config.php=%"
Снова это составляет синтаксис замены подстроки с немедленным расширением.
Примечание: этот подход не работает в случае, если значение dest_file_filename
начинается с %
, ~
, или же *
(но в любом случае это запрещенный символ для имен файлов), или если он содержит =
! Кроме того, если присутствует, "
может также вызвать проблемы (но такие также не допускаются в именах файлов)!
Альтернативный метод для введения второй фазы синтаксического анализа, необходимой для расширения вложенных переменных, заключается в использовании for
цикл, как это:
for /F delims^=^ eol^= %%K in ("!dest_file_filename!") do set "dest_file_dir=!dest_file_full:%%K=!"
Вот, !dest_file_filename!
расширяется, когда весь for /F
цикл анализируется; вторая фаза разбора применяется к телу цикла и получает значение dest_file_filename
(например, config.php
) вместо %%K
,
Преимущество этого метода заключается в том, что немедленное расширение полностью исключается, что может привести к проблемам в случае "
может произойти в значении dest_file_filename
; Однако это создает проблемы с !
а также ^
(они должны быть экранированы, предшествуя ^
).
Значение dest_file_filename
все еще не должен начинаться с ~
или же *
и все еще не должен содержать =
, Это также не должно начинаться с !
,
FOR %%y IN ("!dest_file_full!") DO set "dest_file_dir=%%~dpy"
должен получить ваш целевой диск + путь + терминал \
в dest_file_dir
,
Излишне говорить, без сомнения, ::
Форма комментария не должна использоваться в блоке.
Манипулирование строками в (кодовых блоках) иногда сбивает с толку. Я лично предпочитаю поместить их в сабвуфер. Этот пакет временно отключает приглашение и Echo, чтобы получить лучший результат ваших изменений:
@echo off
setlocal enabledelayedexpansion
Set MyPrompt=%Prompt%
Prompt $S
Echo on
set "source=input dir"
set "target=output dir"
for /f "tokens=* usebackq" %%A in ("file_list.txt") do call :sub "%%A"
pause
Prompt %MyPrompt%
Goto :Eof
:Sub
set "FILE=%~1"
set "dest_file_full=%target%\!FILE:%target%=!"
set "dest_file_filename=%~nx1"
set "dest_file_dir=!dest_file_full:%dest_file_filename%=!"
Rem if not exist "%dest_file_dir%" md "%!dest_file_dir%"
set "source_file_full=%source%\%FILE%"
@Echo copy "%source_file_full%" "%dest_file_dir%"
Особенно второй набор мне неясен, более реальные переменные должны помочь.