我有一个批处理文件,我在目录中显示所有* .pem文件,为用户提供选择一个文件的选项,此时只是尝试使用他们选择的数字向用户显示他们选择的文件到ECHO数组的内容。我相信不知何故因为它在IF语句中,索引变量" selectedPem"没有在数组的索引括号中扩展,因为它使用%并且没有使用延迟扩展!,这似乎不适用于数组的索引括号。我想这是因为如果我设置" selectedPem" IF语句之前的变量,它正确的ECHO。我认为这个工作的唯一选择是以某种方式利用子程序。为什么selectedPem变量在IF语句中不起作用?
@echo off
setlocal enabledelayedexpansion
set dir=%~dp0
cd %dir%
ECHO Performing initial search of private and public keys...
set pemI=0
set keyI=0
for %%p in (*.pem) do (
REM echo %%~nxp
set pems[%pemI%]=%%~nxp
REM echo !pems[%pemI%]!
set /a pemI=%pemI%+1
)
REM echo %pems[0]%
set /a totalPems=%pemI%-1
REM This is the test selectedPem variable that showed me the variable works
REM if I set it outside
REM of the "if defined pems[0]" statement
REM set selectedPem=
if defined pems[0] (
echo PEM Files Found:
for /l %%p in (0,1,%totalPems%) do (
echo %%p^) !pems[%%p]!
)
ECHO Select a pem file to be used as your private key. Otherwise, press
Q to not select one.
set /P selectedPem=Enter a number or Q:
IF "!selectedPem!"=="q" (
ECHO Skipping private key selection.
) ELSE (
ECHO !selectedPem!
ECHO %pems[0]%
REM The below ECHO only works when the above selectedPem is set
REM outside of the "IF defined" statement
ECHO !pems[%selectedPem%]!
REM Tried these other implementations:
REM ECHO !pems[!!selectedPem!!]!
REM ECHO %pems[!selectedPem!]%
REM setlocal disabledelayedexpansion
REM ECHO %pems[%%selectedPem%%]%
set privatekey=!pems[%selectedPem%]!
ECHO You chose %privatekey%
)
)`
输出:
Performing initial search of private and public keys...
PEM Files Found:
0) brandon.pem
Select a pem file to be used as your private key. Otherwise, press Q to not
select one.
Enter a number or Q:0
0
brandon.pem
ECHO is off.
You chose
我理解" ECHO已关闭。"而是输出,因为它将我的数组变量引用解释为空。 感谢您抽出时间阅读我的剧本,我可能过于复杂了。
答案 0 :(得分:1)
您似乎非常了解delayed expansion,因为您大部分时间都在使用它。
但是,你的代码中我通常会调用嵌套变量,因为批处理脚本中没有真正的数组,因为每个数组元素只是一个单独的标量变量(pems[0]
,pems[1]
, pems[2]
等);所以我们也可以将这样的东西称为伪数组。
无论如何,像echo !pems[%selectedPem%]!
这样的东西可以正确扩展,除非它被放置在selectedPem
之前更新的代码行或代码块中,因为那时我们需要{{1}的延迟扩展也是。 selectedPem
不起作用,因为它会尝试扩展变量echo !pems[!selectedPem!]!
和pems[
,而]
会被解释为文字字符串。
在继续前进之前,让我们先回到selectedPem
:为什么这可以扩展?好吧,诀窍是我们在这里有两个扩展阶段,正常或立即扩展(echo !pems[%selectedPem%]!
),然后是延迟扩展(%
)。重要的是序列:嵌套变量的内部一个(因此数组索引)必须在外部变量之前消耗(因此数组元素)。像!
这样的东西无法正确扩展,因为(很可能)没有名为echo %pems[!selectedPem!]%
的变量,并且在将其扩展为空字符串后,两个pems[!selectedPem!]
消失,因此延迟扩展阶段永远不会看到它们。
现在让我们更进一步:在同样更新!
的代码行或代码块中展开类似于echo !pems[%selectedPem%]!
的内容,我们必须避免立即扩展。我们已经知道延迟扩展不能嵌套,但还有其他一些扩展:
call
command在延迟扩张后引入另一个扩张阶段,再次处理selectedPem
- 标志;所以完整的序列是立即扩展,延迟扩展和另一个%
- 扩展。忽略第一个,让我们以嵌套变量的内部一个在外部变量之前展开的方式利用后两个,意思是:%
。要跳过立即扩展阶段,我们只需将call echo %%pems[!selectedPem!]%%
符号加倍,每个符号由一个文字替换,因此我们在立即扩展后会%
。下一步是延迟扩张,然后是下一个echo %pems[!selectedPem!]%
- 扩张
您应该注意%
方法非常慢,因此大量使用会大大降低脚本的整体性能。for
loop变量的扩展发生在立即扩展之后,但是在延迟扩展之前。因此,让我们只围绕迭代一次call
循环并返回for
的值,如下所示:selectedPem
。这是有效的,因为for %%Z in (!selectedPem!) do echo !pems[%%Z]!
不会访问文件系统,除非出现通配符for
或*
,不应该在变量名或(伪)数组索引中使用。 ?
代替标准for
循环:for /F %%Z in ("!selectedPem!") do echo !pems[%%Z]!
(如果"delims="
预计包含selectedPem
,则不需要for /L %%Z in (!selectedPem!,1,!selectedPem!) do echo !pems[%%Z]!
之类的选项字符串只是一个索引号)
也可以使用for /F
(对于数字索引,当然):%
。for /L
loop建立的隐式扩展,意味着读取变量不需要!
和/A
,也可以使用,但前提是数组元素包含数值(请注意set /A
代表算术)。由于这是set /A "pem=pems[!selectedPem!]" & echo !pem!
特有的功能,因此在命令执行期间会发生这种扩展,而这种扩展又会在延迟扩展后发生。所以我们可以这样使用:set /A
。cmd
,在for /F %%Z in ('set /A "selectedPem"') do echo !pems[%%Z]!
而不是批处理上下文中执行时,在控制台上输出(最后)结果;鉴于这构成了索引号,我们可以通过set /A
command捕获它并扩展数组元素,如下所示:set /A
。 cmd
在新的for /F
实例中执行cd /D
,因此这种方法当然不是最快的。有一篇关于这个主题的精彩而全面的文章,你应该仔细阅读它:for /F
。
对于解析命令行和批处理脚本以及一般的变量扩展,请参阅这个很棒的主题:Arrays, linked lists and other data structures in cmd.exe (batch) script
现在让我们来看看你的代码。有几个问题:
cd
代替dir
,以便在必要时更改驱动器;顺便说一下,临时变量cd
不是必需的(让我建议不要使用等于内部或外部命令的变量名以便于阅读),只需在路径上立即使用set pems[%pemI%]=%%~nxp
; < / LI>
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!
)
)
备注):
{{1}}
我不得不承认我没有详细检查你的脚本的逻辑。
答案 1 :(得分:0)
对于一些goto,你可以避免大多数(代码块)。
如果无法做到这一点,请使用!
以及通过电话进行替代延迟展开并正确加倍%%
。
未测试:
@echo off
setlocal enabledelayedexpansion
set dir=%~dp0
cd %dir%
ECHO Performing initial search of private and public keys...
set pemI=0
set keyI=0
for %%p in (*.pem) do (
REM echo %%~nxp
set pems[!pemI!]=%%~nxp
REM call echo %%pems[!pemI!]%%
set /a pemI+=1
)
REM echo %pems[0]%
set /a totalPems=%pemI%
REM This is the test selectedPem variable that showed me the variable works
REM if I set it outside
REM of the "if defined pems[0]" statement
REM set selectedPem=
if pemI==0 (Echo no *.pem files found & goto :End)
echo PEM Files Found:
for /l %%p in (0,1,%totalPems%) do (
echo %%p^) !pems[%%p]!
)
ECHO Select a pem file to be used as your private key. Otherwise, press
Q to not select one.
set /P selectedPem=Enter a number or Q:
IF /I "!selectedPem!"=="q" (ECHO Skipping private key selection.& goto :End
ECHO !selectedPem!
ECHO %pems[0]%
REM The below ECHO only works when the above selectedPem is set
REM outside of the "IF defined" statement
ECHO !pems[%selectedPem%]!
REM Tried these other implementations:
REM ECHO !pems[!!selectedPem!!]!
REM ECHO %pems[!selectedPem!]%
REM setlocal disabledelayedexpansion
REM ECHO %pems[%%selectedPem%%]%
set privatekey=!pems[%selectedPem%]!
ECHO You chose %privatekey%
)
:end
REM ddo wahatever
pause