问题和解决方案。

问题和解决方案。

我想运行一行代码来获取字符串的哈希值,并最终将其与稍后可以在命令末尾复制的哈希值进行比较。我相信,一旦我弄清楚我的输出有什么问题,我就能弄清楚如何进行比较。目前的目标是使用 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 都会将这些命令打印到终端。这是完全正常的。但是,这很令人困惑和误导。要更改此行为,您需要首先发出一个。如下所示:doforECHO 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将比较字符串firstLine1并确定它们不相同……因为它们当然不一样。但你不能只是把放在%一起firstLine。变量的延迟扩展有一个陷阱……你想使用!not %。所以那个条件应该是!firstLine!==1

您还需要更正echo %x%echo !x!

实际情况是,它回显了它执行的命令……但是 if 条件始终为假,所以它没有执行任何其他操作。命令的回显变成了一种掩饰真正问题的花招。

比较字符串时,用方括号 ( []) 括起来也是一种很好的做法。大多数资料都建议改用双引号",但是,这会导致 DOS/CMD 解析双引号时出现许多问题。如果像这里一样什么都不用,当变量根本没有值时就会出现问题。例外情况是当您使用数字比较运算符(例如 或 )执行数字比较EQUGTR

这将为您提供:

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 enabledelayedexpansionendlocal,然后将单行代码放在双引号中即可。

这是起什么作用的?

CMD是运行命令提示符的命令。您实际上可以在命令提示符内运行命令提示符。在这种情况下,它可以在运行时本地化我们设置的任何环境变量。

/V:ON将启用在此新的嵌套命令提示符下运行的所有命令的延迟扩展。

/C告诉 CMD 双引号后面是您想要在这个新的嵌套命令提示符中执行的命令。

因此,这将在启用延迟扩展的新嵌套命令提示符中运行您的单行代码。太棒了!但还不够。

For选项之间必须有空格

"skip=1delims="需要是"skip=1 delims="

代码块i中缺失firstLineif

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!

相关内容