![](/img/trans.png)
[英]Printing array in batch file using variable index, having trouble with delayed expansion
[英]Array of file names inside a for loop with delayed expansion (windows batch file)
我正在尝试将我的硬盘中的文件同步到我的MEGA帐户( https://mega.nz/ ),这是一个免费的加密在线数据存储服务。 不幸的是,MEGA还没有发布任何Windows命令行实用程序,他们当前的Windows同步客户端( https://mega.nz/#sync )需要做很多工作。 我尝试上传时,它删除了所有本地文件。 我能够在GitHub上找到一些名为Megatools( https://megatools.megous.com/ )的第三方实用程序,它们的功能严重不足。 例如:复制功能不允许您选择使用硬盘驱动器中的较新版本覆盖MEGA服务器上的文件。 因此,我试图通过编写Windows批处理文件程序来扩展Megatools。
该程序非常简单:它使用FORFILES
( http://ss64.com/nt/forfiles.html )扫描本地同步文件夹,然后比较文件名,相对路径,大小和修改日期等属性与列表远程文件。 使用Megatools megals.exe
函数快速填充远程文件。 属性存储在数组中。 请注意,我知道Windows批处理文件不会像其他编程语言那样处理数组,这对编译器来说只是另一个变量。
因为FORFILES
作为基本for循环,我使用索引数组来存储信息,所以我需要启用Delayed Expansion以使索引FORFILES
运行。 我真的不太了解延迟扩展。 我已经尝试过很多研究,但这对我没有任何意义。 由于Delayed Expansion的语法涉及使用!var!
封装变量!var!
而不是%var%
,我的文件名可能包含!
符号,我不知道该怎么做。
我研究了这个问题,显然解决方案是使用SETLOCAL DisableDelayedExpansion
和ENDLOCAL
语句临时禁用延迟扩展,但这涉及将使用SET
语句创建的任何变量的范围限制为SETLOCAL
和ENDLOCAL
之间的行。
我发现这个问题的一个解决方案是使用& SET newvar=%oldvar%
追加ENDLOCAL
,但每当我尝试运行程序时newvar
仍为空!
@ECHO off
SETLOCAL EnableDelayedExpansion
REM ...
REM (Parameter processing code, irrelevant)
REM ...
SET localpathempty=UNSET
SET numlocal=UNSET
CALL:islocalpathempty
IF "%localpathempty%"=="FALSE" (
ECHO Items found in local directory. Gathering file names...
SETLOCAL DisableDelayedExpansion
FOR /F "tokens=*" %%a IN ('FORFILES /S /P "%localpath%." /C "cmd /c echo @FILE"') DO (
SET /A "j+=1"
SET "line=%%~a"
SETLOCAL EnableDelayedExpansion
SET localfilename[!j!]=!line!
ECHO Array Index inside 'LOCAL': !j!
ENDLOCAL
ECHO Array Index outside 'LOCAL': %j%
)
ENDLOCAL & SET numlocal=%j%
)
IF "%localpathempty%"=="TRUE" (
ECHO WARNING: LOCAL PATH %localpath% IS EMPTY. PROCESSING SKIPPED.
)
ECHO Final Index: %j%
ECHO Total Items: %numlocal%
REM ...
REM (Further processing of local files and remote files)
REM ...
ENDLOCAL
GOTO :eof
REM ...
REM (functions)
REM ...
Items found in local directory. Gathering file names...
Array Index inside 'LOCAL': 1
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 2
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 3
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 4
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 5
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 6
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 7
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 8
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 9
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 10
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 11
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 12
Array Index outside 'LOCAL':
Array Index inside 'LOCAL': 13
Array Index outside 'LOCAL':
Final Index:
Total Items:
如您所见,在第二个SETLOCAL EnableDelayedExpansion
语句之前设置的索引不能在该代码块中存活。 我需要能够记录最终索引以及文件名。 我没有尝试通过第二个SETLOCAL EnableDelayedExpansion
和ENDLOCAL
语句传递文件名,因为索引甚至没有生存! 任何帮助将非常感谢这个问题。 我意识到我的语法可能有一些问题,但我已经尽了最大努力。 索引消失对我来说是一个巨大的谜。
PS:如果你想知道为什么我开始启用延迟扩展而不是禁用,原因是因为后来发生的绝大多数代码(此处未显示)要求启用它。
在完全处理命令行之前,首先将用%
name of variable %
引用的环境变量的字符串值插入命令行。
所以就行了
echo Path extensions are: %PATHEXT%
Windows命令处理器首先用环境变量PATHEXT的字符串值替换%PATHEXT%
,然后THEN解释调用内部函数echo
的行,该函数将不再包含%PATHEXT%
的字符串写入标准流标准输出 。
Windows命令处理器将以(
以#结尾)
开始的块解释为跨越多行的命令行。 因此Windows命令处理器在查找(
正在开始一个块搜索匹配)
。 然后,当前行和此块中所有出现的%
name变量 %
都被引用变量的当前字符串值替换。
如果在这样的块内定义或修改变量,则在处理行之前的这种变量扩展通常会产生意外行为。 在执行块中的任何命令之前,整个块一旦被命令处理器解析,因此在执行块的行时不再考虑处理行期间的环境变量的更新。
这就是为什么只要在以(
以#结尾)
开始的块中定义或修改变量就需要延迟扩展的原因,并且当前变量值也在该块内进行评估。
注意:循环变量不是环境变量。 循环变量总是“扩展延迟”。
在命令提示符窗口中从批处理文件的第一行中删除了@ECHO OFF
的批处理文件时,可以很容易地看到变量 %
的%
名称替换变量的当前字符串值,因为Windows命令处理器输出的是真正的在每一行处理。
但是setlocal EnableDelayedExpansion
不仅仅支持延迟扩展!
现在任何地方都有特殊意义(包括echo Hello!
),它还会生成当前环境表的副本,并推送延迟扩展的当前状态,扩展的使用状态以及堆栈上的当前目录。 稍后当显式或隐式调用endlocal
时,将丢弃当前环境表,并从堆栈中恢复延迟扩展的状态,扩展的使用状态和当前目录。
换句话说, setlocal
始终是一个完整的本地环境。
使用块来避免混淆的简单解决方案通常是使用setlocal EnableDelayedExpansion
在批处理文件的顶部启用延迟扩展,使用endlocal
显式或隐式地恢复批处理文件底部的先前环境,以及引用环境变量(不是循环变量或参数)传递给批处理文件或子程序)使用!
...... !
而不是%
... %
。 启用延迟扩展功能始终是安全的!
...... !
即使引用的变量未在块中修改,也可以代替%
... %
。
但是,通过使用GOTO和CALL ,可以始终在不使用块的情况下编写批处理文件。 因此,经常可以编写批处理文件而无需使用延迟扩展。
以下是您不使用块重写的代码。
@ECHO off
REM ...
REM (Parameter processing code, irrelevant)
REM ...
SET LocalPathEmpty=UNSET
SET NumLocal=UNSET
SET FileIndex=0
CALL :IsLocalPathEmpty
IF "%LocalPathEmpty%"=="TRUE" GOTO NoLocalPath
IF "%LocalPathEmpty%"=="FALSE" GOTO ProcessFiles
REM There is something wrong with subroutine IsLocalPathEmpty.
GOTO :EOF
:ProcessFiles
ECHO Items found in local directory. Gathering file names...
FOR /R "%LocalPath%" %%a IN (*) DO CALL :AddLocalFile "%%~a"
GOTO OutputSummary
:NoLocalPath
ECHO WARNING: LOCAL PATH %LocalPath% IS EMPTY. PROCESSING SKIPPED.
:OutputSummary
ECHO Final Index: %FileIndex%
ECHO Total Items: %NumLocal%
REM ...
REM (Further processing of local files and remote files)
REM ...
GOTO :EOF
REM ...
REM (functions)
REM ...
:AddLocalFile
SET /A FileIndex+=1
SET "LocalFileName[%FileIndex%]=%~1"
EXIT /B
请注意,我无法在语法错误上测试此代码,因为它不可执行。
set /?
在命令提示符窗口内执行,输出多个帮助页面,解释IF和FOR块上正常和延迟环境变量扩展的差异。
另请参阅批量回复URL的答案以及为什么我的cd%myVar%被忽略?
我也通过一个简单的for
循环替换了forfiles
for
因为这个命令也可以递归地列出指定目录中的所有文件。 运行for /?
在命令提示符窗口中获取有关for /R
语法的帮助和详细信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.