Вы, похоже, хорошо знаете о отложенном расширении , так как вы используете его в большинстве случаев.
Однако то, что у вас есть в коде, я обычно называю вложенными переменными, потому что в пакетном скриптинге нет реальных массивов, поскольку каждый элемент массива - это не что иное, как отдельная скалярная переменная (pems[0]
, pems[1]
, pems[2]
, так далее.); так что мы можем называть что-то подобное также псевдо-массивами.
В любом случае, что-то вроде echo !pems[%selectedPem%]!
может быть правильно расширено, если только оно не помещено в строку или блок кода, в котором selectedPem
обновляется раньше, потому что тогда нам также потребуется задержанное расширение для selectedPem
. echo !pems[!selectedPem!]!
не работает, потому что он пытается раскрыть переменные pems[
и ]
, а selectedPem
интерпретируется как буквенная строка.
Прежде чем двигаться вперед, давайте сначала вернемся к echo !pems[%selectedPem%]!
: почему это расширяется? Хитрость в том, что у нас есть две фазы расширения: нормальное или немедленное расширение (%
), за которым следует отложенное расширение (!
). Важной вещью является последовательность: внутренняя из вложенных переменных (отсюда и индекс массива) должна быть израсходована перед внешней (поэтому элемент массива). Нечто подобное echo %pems[!selectedPem!]%
не может быть правильно развернуто, потому что (скорее всего) нет переменной с именем pems[!selectedPem!]
, и после развертывания ее в пустую строку два !
исчезают, и поэтому фаза отложенного расширения никогда их не увидит.
Теперь давайте сделаем еще один шаг: чтобы развернуть нечто подобное echo !pems[%selectedPem%]!
внутри строки или блока кода, который также обновляет selectedPem
, мы должны избегать немедленного расширения. Мы уже узнали, что отложенное расширение не может быть вложенным, но есть некоторые другие расширения:
- Команда
call
вводит еще одну фазу расширения после отложенного расширения, которая снова обрабатывает %
-знаки; поэтому полная последовательность - это немедленное расширение, отложенное расширение и другое %
-разложение. Отклоняя первое, давайте используем последние два таким образом, что внутренняя из вложенных переменных будет расширена перед внешней, что означает: call echo %%pems[!selectedPem!]%%
. Чтобы пропустить фазу немедленного расширения, мы просто удваиваем %
знаков, которые заменяются буквальными, поэтому у нас есть echo %pems[!selectedPem!]%
после немедленного расширения. Следующим шагом является отложенное расширение, а затем упомянутое следующее %
-расширение.
Обратите внимание, что метод call
довольно медленный, поэтому его интенсивное использование может значительно снизить общую производительность вашего сценария.
- Расширение переменных
for
loop происходит после немедленного расширения, но перед отложенным расширением. Итак, давайте обернем цикл for
, который повторяется только один раз и возвращает значение selectedPem
, например: for %%Z in (!selectedPem!) do echo !pems[%%Z]!
. Это работает, потому что for
не обращается к файловой системе, пока не появится подстановочный знак *
или ?
, который в любом случае не должен использоваться в именах переменных или (псевдо) массивах.
Вместо стандартного цикла for
можно также использовать for /F
: for /F %%Z in ("!selectedPem!") do echo !pems[%%Z]!
(не требуется строка параметра, например "delims="
, если ожидается, что selectedPem
будет содержать только индексный номер ).
Также можно использовать for /L
loop (для числовых индексов, конечно): for /L %%Z in (!selectedPem!,1,!selectedPem!) do echo !pems[%%Z]!
.
- Неявное расширение, установленное командой
set /A
, означающее, что ни %
, ни !
не требуется для чтения переменных, также можно использовать, но только если элемент массива содержит числовое значение (обратите внимание, что /A
обозначает арифметику). Поскольку это особенность, характерная для set /A
, такое расширение происходит во время выполнения команды, что, в свою очередь, происходит после отложенного расширения. Таким образом, мы можем использовать это так: set /A "pem=pems[!selectedPem!]" & echo !pem!
. - Просто для полноты, вот еще один способ:
set /A
при выполнении в cmd
, а не в пакетном контексте, выводит (последний) результат на консоль; учитывая, что это составляет порядковый номер, мы можем записать его на for /F
и расширить элемент массива следующим образом: for /F %%Z in ('set /A "selectedPem"') do echo !pems[%%Z]!
. set /A
выполняется в новом cmd
экземпляре for /F
, поэтому этот подход, конечно, не самый быстрый.
Существует большой и всеобъемлющий пост, посвященный этой теме, который вы должны подробно изучить: Массивы, связанные списки и другие структуры данных в сценарии cmd.exe (пакетная обработка) .
Для синтаксического анализа командных строк и пакетных сценариев, а также раскрытия переменных в целом обратитесь к этому удивительному потоку: Как анализирует сценарии интерпретатора команд Windows (CMD.EXE)?
Теперь давайте посмотрим на ваш код. Есть несколько вопросов:
- используйте
cd /D
вместо cd
, чтобы при необходимости также заменить привод; кстати, временная переменная dir
не нужна (позвольте мне рекомендовать не использовать имена переменных, которые равны внутренним или внешним командам для удобства чтения), просто сразу используйте cd
на пути;
- вы пропустили использование отложенного расширения в строке
set pems[%pemI%]=%%~nxp
, оно должно читать set pems[!pemI!]=%%~nxp
, так как pemI
обновляется в окружающем цикле for
;
- вам нужно либо задержанное расширение в строке
set /a pemI=%pemI%+1
, либо вы используете неявное расширение переменной set /A
, поэтому set /A pemI=pemI+1
будет работать; однако это даже можно упростить: set /A pemI+=1
, что полностью эквивалентно;
- Я бы использовал сравнение без учета регистра, особенно когда речь идет о вводе пользователем, например, проверка на
Q
, которая будет if /I "!selectedPem!"=="q"
;
- Теперь мы подошли к линии, которая нуждается в том, что мы узнали выше:
ECHO !pems[%selectedPem%]!
должно стать call echo %%pems[!selectedPem!]%%
; альтернативный способ использования for
также вставлен в комментарий (rem
);
- затем есть еще одна строка с той же проблемой:
set privatekey=!pems[%selectedPem%]!
должен стать call set privatekey=%%pems[!selectedPem!]%%
(или подход, основанный также на for
);
- еще раз вы пропустили использование отложенного расширения, на этот раз в строке
ECHO You chose %privatekey%
, которая должна читать echo You chose !privatekey!
;
- наконец-то я улучшил цитату вашего кода, в частности команду
set
, которую лучше написать как set "VAR=Value"
, чтобы любые невидимые конечные пробелы не становились частью значения, а само значение становилось защищен, если содержит специальные символы, но кавычки не становятся частью значения, что может особенно помешать загрязнению;
Итак, вот фиксированный код (со всеми вашими rem
замечаниями):
@echo off
setlocal EnableDelayedExpansion
cd /D "%~dp0"
echo Performing initial search of private and public keys...
set "pemI=0"
set "keyI=0"
for %%p in (*.pem) do (
set "pems[!pemI!]=%%~nxp"
set /A "pemI+=1"
)
set /A "totalPems=pemI-1"
if defined pems[0] (
echo PEM Files Found:
for /L %%p in (0,1,%totalPems%) do (
echo %%p^) !pems[%%p]!
)
set /P selectedPem="Enter a number or Q: "
if /I "!selectedPem!"=="q" (
echo Skipping private key selection.
) else (
echo !selectedPem!
echo %pems[0]%
call echo %%pems[!selectedPem!]%%
rem for %%Z in (!selectedPem!) do echo !pems[%%Z]!
call set "privatekey=%%pems[!selectedPem!]%%"
rem for %%Z in (!selectedPem!) do set "privatekey=!pems[%%Z]!"
echo You chose !privatekey!
)
)
Должен признать, что я не проверил логику вашего сценария подробно.