我想运行一行代码来获取字符串的哈希值,并最终将其与稍后可以在命令末尾复制的哈希值进行比较。我相信,一旦我弄清楚我的输出有什么问题,我就能弄清楚如何进行比较。目前的目标是使用 certutil 打印文件的哈希值,因为它是 Windows 的原生工具。我执行此操作:
setlocal enabledelayedexpansion && set "firstLine=1" & for /f "skip=1delims=" %i in ('certutil -hashfile file.zip SHA512') do (if firstLine==1 (set "x=%i"&set "firstLne=0"&echo "%x%")) & endlocal
最后得到:
C:\Users\John\Downloads>(if firstLine == 1 (set "x=9ba9467f05f1c7fa7161f857b0085461ce28401a2fe01a8062eec2254eaafc4b239fb3dc9298b5df5f27c2bb64618a8606a6885aa171604c541f4d5fe394b361" & set "firstLne=0" & echo "%x%" ) ) & endlocal
C:\Users\John\Downloads>(if firstLine == 1 (set "x=CertUtil: -hashfile command completed successfully." & set "firstLne=0" & echo "%x%" ) ) & endlocal
C:\Users\John\Downloads>
看起来 do 之后的所有内容都以字符串形式再次执行。第一行输出被跳过,但由于 if 语句中的所有内容都无法执行,因此 for 语句的后两行被“执行”。
答案1
在调试代码时,将代码展开成单独的行会很有帮助。您甚至可以将这些行放在“批处理”文件中来运行它并测试您解决问题的尝试。为了便于阅读,我将展示我所做的更改,这些更改展开成多行。
作为参考,将您的代码扩展为多行,如下所示:
setlocal enabledelayedexpansion && set "firstLine=1"
for /f "skip=1delims=" %i in ('certutil -hashfile file.zip SHA512') do (
if firstLine==1 (
set "x=%i"
set "firstLne=0"
echo "%x%"
)
)
endlocal
请注意,要将上述内容放入批处理文件中,您还需要替换%i
。DOS %%i
/CMD 就是这么奇怪。
问题和解决方案。
您的问题实际上有两个问题……其中一个问题掩盖了另一个问题。
误导您的问题是您打开了“echo”。解释一下,在循环的每次迭代中,for
将后面的所有内容作为命令执行一次。在 DOS/CMD 中,命令在执行之前会回显到终端。因此,每次执行您给出的命令时,CMD 都会将这些命令打印到终端。这是完全正常的。但是,这很令人困惑和误导。要更改此行为,您需要首先发出一个。如下所示:do
for
ECHO OFF
echo off
setlocal enabledelayedexpansion && set "firstLine=1"
for /f "skip=1delims=" %i in ('certutil -hashfile file.zip SHA512') do (
if firstLine==1 (
set "x=%i"
set "firstLne=0"
echo "%x%"
)
)
endlocal
结果将不会有任何输出。这是因为真正的问题。该问题是您的 if 语句的条件。您遗漏了“变量标记”。
firstLine==1
将比较字符串firstLine
和1
并确定它们不相同……因为它们当然不一样。但你不能只是把放在%
一起firstLine
。变量的延迟扩展有一个陷阱……你想使用!
not %
。所以那个条件应该是!firstLine!==1
。
您还需要更正echo %x%
为echo !x!
实际情况是,它回显了它执行的命令……但是 if 条件始终为假,所以它没有执行任何其他操作。命令的回显变成了一种掩饰真正问题的花招。
比较字符串时,用方括号 ( []
) 括起来也是一种很好的做法。大多数资料都建议改用双引号"
,但是,这会导致 DOS/CMD 解析双引号时出现许多问题。如果像这里一样什么都不用,当变量根本没有值时就会出现问题。例外情况是当您使用数字比较运算符(例如 或 )执行数字比较EQU
时GTR
。
这将为您提供:
echo off
setlocal enabledelayedexpansion && set "firstLine=1"
for /f "skip=1delims=" %i in ('certutil -hashfile file.zip SHA512') do (
if [!firstLine!]==[1] (
set "x=%i"
set "firstLne=0"
echo "!x!"
)
)
endlocal
但由于其他几个原因,将其变成一行代码不太适用:
其他关键问题
SETLOCAL
仅适用于批处理文件
引用命令本身的帮助文本:
C:\Users\wolferz>help setlocal
开始在批处理文件中本地化环境变化。 发出 SETLOCAL 后所做的环境更改仅限于批处理文件。必须发出 ENDLOCAL 才能恢复以前的设置。到达批处理脚本的末尾时,将针对该批处理脚本发出的任何未完成的 SETLOCAL 命令执行隐含的 ENDLOCAL。
但是!有一种解决方法是在命令行中使用单行代码的延迟扩展,同时保持变量在该行代码的执行范围内。只需在命令前加上CMD /V:ON /C
,删除setlocal enabledelayedexpansion
和endlocal
,然后将单行代码放在双引号中即可。
这是起什么作用的?
CMD
是运行命令提示符的命令。您实际上可以在命令提示符内运行命令提示符。在这种情况下,它可以在运行时本地化我们设置的任何环境变量。
这/V:ON
将启用在此新的嵌套命令提示符下运行的所有命令的延迟扩展。
并/C
告诉 CMD 双引号后面是您想要在这个新的嵌套命令提示符中执行的命令。
因此,这将在启用延迟扩展的新嵌套命令提示符中运行您的单行代码。太棒了!但还不够。
For
选项之间必须有空格
"skip=1delims="
需要是"skip=1 delims="
。
代码块i
中缺失firstLine
if
firstLne=0
需要是firstLine=1
固定的一行代码
此时我们有:
echo off
set "firstLine=1"
for /f "skip=1 delims=" %i in ('certutil -hashfile file.zip SHA512') do (
if [!firstLine!]==[1] (
set "x=%i"
set "firstLine=0"
echo "!x!"
)
)
这浓缩为一行 (带有CMD /V:ON /C
):
cmd /v:on /c "echo off & set "firstLine=1" & for /f "skip=1 delims=" %i in ('certutil -hashfile file.zip SHA512') do ( if [!firstLine!]==[1] ( set "x=%i" & set "firstLine=0" & echo "!x!"" ) )
现在我不能保证这会完全按照你的要求去做......但它不会因为语法错误而失败。
另外,最后一点建议:学习 PowerShell,而不是 DOS/CMD。除了 DOS/CMD 受到数十年的问题和向后兼容性导致的语法混乱的困扰之外……它也不是现代 Windows 安装中的默认 shell,并且很可能在未来十年内被完全废弃。PowerShell 是一个全新的开始,具有更智能的语法和更强大的脚本功能。而且,随着 PowerShell Core 6 的出现,它现在可以移植到 Windows 以外的平台。
答案2
- 好吧,如果您需要一行来比较 SHA512 字符串,并且这是先前已知的:
cmd/v/c ""%__APPDIR__%certutil.exe" -hashfile 7z1900-x64.exe SHA512|find "af8f38679e16c996ffac152cac49369cf4b609abbd2cad07f49a114a82c6b5e564be29630c0fd2418110cf1a3d0ef3c9cc12f9164a69a575c91d9b98ce0df1a9">nul&&@echo\yEp!"||@echo\nOp!
- 在命令行或者bat/cmd文件中使用:
cmd /v /c " "%__APPDIR__%certutil.exe" -hashfile file.ext SHA512|find "Your String SHA512">nul && @echo\yEp!" || @echo\nOp!