Редактирование слов в строке с использованием командного файла - PullRequest
0 голосов
/ 06 сентября 2018

Это то, что я написал до сих пор:

for /l %%x in (Veronica, Nils, Mike, Tom) do (
set word=%%x
set str="My name is Name"
call set str=%%str:Name=%word%%%
echo %str% >> names.txt
)

К сожалению, единственный вывод:

ECHO is on.
ECHO is on.
ECHO is on.

в бесконечном цикле.

Любые идеи о том, где я ошибся /

Ответы [ 3 ]

0 голосов
/ 06 сентября 2018

Возможно, вы захотите посмотреть delayedexpansion run из cmdline setlocal /?, чтобы увидеть, как он включен, и set /?, чтобы увидеть его объяснение:

@echo off
setlocal enabledelayedexpansion
for %%x in (Veronica, Nils, Mike, Tom) do (
  set "word=%%x"
  set "str=My name is repl"
  call set "str=%%str:repl=!word!%%"
  echo !str!
)

Чтобы скрипт выглядел чище, вы можете даже захотеть добавить имена в переменную, которую можно обновить, без необходимости расширять цикл for, вызвав переменную:

@echo off
set "names=Veronica,Nils,Mike,Tom"
setlocal enabledelayedexpansion
for %%x in (%names%) do (
  set "word=%%x"
  set "str=My name is repl"
  call set "str=%%str:repl=!word!%%"
  echo !str!
)

Обратите внимание, что мы заменили % на !, где нам нужны расширенные переменные. Также не то, чтобы я устанавливал свои двойные кавычки, начинающиеся с имени переменной в каждом set

0 голосов
/ 06 сентября 2018

Какой-то пункт в вашем коде:

Ошибка:

  • Синтаксис FOR /L %var IN (start,step,end) используется для перебора последовательности чисел от start до end с увеличением на величину, указанную step Таким образом, переменная цикла (%var) представляет текущий номер в последовательности.
    Но вы хотите перебрать набор строк, для этого вам нужно использовать голый вариант команды FOR (FOR без каких-либо переключателей). В документации сказано, что она предназначена для итерации набора файлов, но пока строки не содержат подстановочных знаков *?, ее можно безопасно использовать для итерации набора строк.

    Таким образом, в качестве первого шага для исправления кода необходимо удалить переключатель /L, команду FOR:
    for %%x in (Veronica, Nils, Mike, Tom) do ...

  • Нормальные переменные расширения; Также известный как процентное расширение переменной, происходит до того, как код будет фактически выполнен, поэтому в блоке кода вы можете надежно использовать процентное расширение только для переменных, которые были определены до ввода этого блока кода, любое изменение переменных в том же блоке не будет виден при расширении в процентах (%var%), потому что оно было расширено до того, что было до входа в блок.

    Чтобы преодолеть это ограничение, вы можете либо удвоить проценты вокруг переменной и добавить префикс команды с CALL: call echo %%var%% или используйте расширение переменной с задержкой: echo !var!

    Таким образом, следующим шагом будет замена echo %str% на call echo %%str%%.

  • При подстановке переменных регистр не учитывается, поэтому в подстановке %str:Name=Vernica%, поскольку переменная str содержит два вхождения, подстрока "name" ("My name is Name") заменяет обе, и результат будет быть My Veronica is Veronica. Для замены части Name вы должны использовать подстроку, которая уникальна в основной строке:
    set "str=My name is $Name"
    call set str=%%str:$Name=%%x%%

    Также обратите внимание на прямое использование FOR переменной %%x в замене. Нет необходимости присваивать %%x другой переменной, как то, что вы сделали с set word=%%x. И даже если вы, это не будет работать, потому что в call set str=%%str:Name=%word%%% переменная %word% уже заменена пакетным парсером на любое значение, которое было до входа в блок FOR.

Суммируя вышеприведенные пункты, рабочий код может быть написан так:

for %%x in (Veronica, Nils, Mike, Tom) do (
    set "str=My name is $Name"
    call set str=%%str:$Name=%%x%%
    call echo %%str%% >>"names.txt"
)

Вышеописанное работает правильно и дает желаемый результат, но он крайне неэффективен и может быть дополнительно оптимизирован для получения более эффективного, быстрого, более короткого и чистого кода, который будет описан в следующем разделе.


Неэффективность:

Продолжаем с исправленным кодом в последнем разделе

  • Переменная str является статической переменной, то есть на каждой итерации цикла вы инициализируете ее одной и той же строкой My name is $Name Таким образом, ее можно переместить за пределы цикла и инициализировать только один раз.

  • Поскольку вы используете замещенную строку только один раз для записи ее на устройство вывода, set и echo можно объединить в одну команду, выполнив обе команды одновременно: call echo %%str:$Name=%%x%%

  • Перенаправление, используемое внутри цикла (>>"names.txt"), поэтому на каждой итерации цикла файл names.txt будет открываться, записываться и закрываться. Это очень неэффективно, подумайте о цикле, который делает это десятки тысяч раз. Весь цикл FOR может быть помещен в перенаправленный блок, поэтому файл names.txt будет открыт только один раз, когда цикл запускается, и закрывается, когда цикл завершается.

  • Лучше заключать элементы имени в кавычки, если они содержат пробелы или специальные символы, на которые код не повлияет. На самом деле это не неэффективность, а рекомендация передовой практики.


Конечный результат

Без отложенного расширения:

set "str=My name is $Name"
(for %%x in ("Veronica", "Nils", "Mike", "Tom") do call echo %%str:$Name=%%~x%%)>"names.txt"

С отложенным расширением:

set "str=My name is $Name"
setlocal EnableDelayedExpansion
(for %%x in ("Veronica", "Nils", "Mike", "Tom") do echo !str:$Name=%%~x!)>"names.txt"
endlocal

По сравнению с использованием CALL или DelayedExpansion

  • CALL очень медленно, DelayedExpansion намного быстрее.

  • DelayedExpansion потерпит неудачу, если имена содержат !

  • CALL потерпит неудачу, если имена содержат какие-либо символы &|<>, если вы не заключите их в кавычки при использовании echo. например call echo "%%str:$Name=%%~x%%".
    Конечно, это не может произойти с человеческими именами, но с общими строками.

0 голосов
/ 06 сентября 2018

Командный процессор Windows заменяет все вхождения %variable% в командном блоке, начиная с ( и заканчивая сопоставлением ) текущим значением указанной переменной среды перед выполнением команды с помощью команды блок. См. Как анализирует сценарии интерпретатора команд Windows (CMD.EXE)? Эту обработку блока команд с помощью cmd.exe можно увидеть, запустив пакетный файл из окна командной строки вместо двойного щелчка по пакету. файл с @echo off удален или изменен на @echo ON или закомментирован временно, пока пакетный файл не работает должным образом.

Вывод справки при запуске set /? в окне командной строки объясняет на примере IF и FOR , когда и как использовать отложенное расширение , которое является предпочтительным способом доступа к значению переменной среды в пределах командного блока, определяемого или изменяемого в том же командном блоке.

Другое решение - ссылка на переменную среды с использованием синтаксиса %%variable%% и использование команды CALL для принудительного двойного анализа этой командной строки с помощью cmd.exe. В этом случае %% изменяется при разборе всего командного блока на % с обеих сторон указанной переменной среды, а затем при выполнении команды CALL оставшиеся %variable% анализируются еще раз и заменяются на текущее значение указанной переменной.

В разнесенном коде партии переменная окружения str не определена выше FOR командная строка с блоком команд. По этой причине echo %str% >> names.txt выполняет синтаксический анализ всего командного блока, измененного с cmd.exe на echo >> names.txt до того, как FOR будет выполнен вообще, и поэтому ECHO просто выводит три раза текущее состояние .

Также командная строка call set str=%%str:Name=%word%%% не работает, поскольку word не определена ранее FOR командной строки с блоком команд и, следовательно, %word% уже заменена ничем, что приводит к командной строке call set str=%str:Name=% окончательно выполняется три раза при выполнении цикла.

Следующая ошибка - использование опции FOR /L для этого цикла. Вывод справки при запуске for /? в окне командной строки объясняет для for /L:

FOR /L %variable IN (start,step,end) DO command [command-parameters]

    The set is a sequence of numbers from start to end, by step amount.
    So (1,1,5) would generate the sequence 1 2 3 4 5 and (5,-1,1) would
    generate the sequence (5 4 3 2 1)

После прочтения этой справки должно быть ясно, что /L не подходит для этой задачи.

Следующая ошибка заключается в том, что подстановки строк, выполняемые с помощью команды SET или непосредственно с помощью cmd.exe при разборе командной строки, всегда применяются без учета регистра. Строка, присвоенная переменной окружения str, содержит name и Name, которые заменяются при подстановке строки. Это действительно не нужно здесь.

Тогда есть большая разница при использовании set variable="value" или при использовании set "variable=value". Последнее лучше, как подробно объяснено в ответе на Почему нет строкового вывода с 'echo% var%' после использования 'set var = text' в командной строке?

Наконец, пробел, оставленный оператору перенаправления >>, также выводится командой ECHO , что приводит к завершающему пробелу в файле. Поэтому оператор перенаправления >> не должен отделяться пробелом от строки для записи в файл.

Первый рабочий код для примера использует отложенное расширение переменной среды:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
del names.txt 2>nul
set "str=My name is #name#"
for %%I in (Veronica, Nils, Mike, Tom) do echo !str:#name#=%%~I!>>names.txt
endlocal

Второй рабочий код для примера использует трюк CALL :

@echo off
del names.txt 2>nul
set "str=My name is #name#"
for %%I in (Veronica, Nils, Mike, Tom) do call echo %%str:#name#=%%~I%%>>names.txt

Чтобы понять используемые команды и то, как они работают, откройте окно командной строки, выполните там следующие команды и полностью прочитайте все страницы справки, отображаемые для каждой команды.

  • call /?
  • del /?
  • echo /?
  • endlocal /?
  • for /?
  • setlocal /?
  • set /?

См. Также статью Microsoft о Использование операторов перенаправления команд .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...