【问题标题】:Batch script doesn't run, although its code runs in CMD批处理脚本不运行,尽管它的代码在 CMD 中运行
【发布时间】:2018-12-07 23:20:36
【问题描述】:

我正在寻找一种简单的方法来获取以 GB(批量)为单位的系统卷信息,所以我得到了这个:

for /f "tokens=1-3" %a in ('WMIC LOGICALDISK GET FreeSpace^,Name^,Size ^|FINDSTR /I /V "Name"') do @echo wsh.echo "%b" ^& " free=" ^& FormatNumber^(cdbl^(%a^)/1024/1024/1024, 2^)^& " GiB"^& " size=" ^& FormatNumber^(cdbl^(%c^)/1024/1024/1024, 2^)^& " GiB" > %temp%\tmp.vbs & @if not "%c"=="" @echo(& @cscript //nologo %temp%\tmp.vbs >> c:\test\test2.txt | type C:\test\test2.txt

如果我只是将它复制并粘贴到 CMD 中,它可以正常工作,但如果我从 .bat 文件运行它,它就什么也不做。

我做错了什么?

PD:我不能使用 PowerShell,我需要批量使用。

【问题讨论】:

  • 这不是批处理,这是一次徒劳的尝试,将 vbs 写入单行传递值并将其包装在 for /f 处理 wmic 输出中。 wmic 可以返回正确的值,但批处理仅限于带符号的 32 位计算,即 +/-2GB。为什么不能使用 PowerShell?这两天有2个类似的问题。使用上面的搜索框。
  • 批处理文件要求所有for 元变量的% 符号加倍,例如%%a...顺便说一句,我不明白@ 987654327@;它实际上不应该是& type C:\test\test2.txt吗?
  • 我不能使用 powershell,因为我需要在不同的机器上运行它,其中一些已经禁用了 powershell。 @aschipfl 我将添加缺少的 %,如果我使用“&”符号,它实际上会显示输出两次,但如果我使用“|”,它会按原样显示输出 1 次。不确定“|”是否不是有效符号,我是 linux 人,这是我第一次处理 windows 脚本。
  • 相关:Get size of a directory in 'MB' using batch file。顺便说一句,您不应该查询Name,而是查询DeviceID 值;另见this SU post...

标签: batch-file cmd


【解决方案1】:

您的代码存在以下问题:

  • 在批处理文件中,for 元变量前面必须有 两个 %-符号,而不是命令提示符中的一个 (cmd),因此更改 %a 等., 到%%a, 等等;否则会出现语法错误;
  • 在使用for /Fskip=1 选项时,您可以避免使用findstr /V 过滤掉wmic 输出的标题;
  • 我推荐使用DeviceID,而不是属性Name,根据这个超级用户线程:What is the difference between properties Name, Caption and DeviceID (when executing wmic LogicalDisk)?;
  • 不确定您要查询哪些驱动器,但也许您只需要本地磁盘;如果是,则添加子句where "DriveType=3";参考这篇微软文章:Win32_LogicalDisk class;
  • 临时 VBScript 脚本的创建应在调用它的 if 条件内进行,以避免无意义的文件写入活动;
  • 您在每次循环迭代中都附加到文本文件,但您从未在开始时初始化一个空文件;也许这是故意的行为,但我不这么认为;无论如何,您可以根据自己的喜好对整个 for /F 循环执行一次重定向,或者(过度)写入 (>) 或附加 (>>);
  • type 命令的管道是无用的,因为type 无论如何都不接受任何传入的数据;我猜| 符号应该已经替换为&;尽管如此,type 命令在循环中没有意义,我认为它应该在循环之后执行,而不是显示一次完整的收集数据;
  • echo( 可以被删除,因为它只是向控制台写入一个空行;
  • 最后应清理临时 VBScript 文件;

纠正所有这些事情会导致这样的批处理脚本(为了便于阅读,我没有将它们全部写在一行中):

> "C:\test\test2.txt" (
    for /F "skip=1 tokens=1-3" %%a in ('
        wmic LogicalDisk where "DriveType=3" get DeviceID^,FreeSpace^,Size
    ') do @(
        if not "%%c"=="" (
            > "%TEMP%\tmp.vbs" echo WScript.Echo "%%a" ^& " free=" ^& FormatNumber^(CDbl^(%%b^) / 1024 / 1024 / 1024, 2^) ^& " GiB" ^& " size=" ^& FormatNumber^(CDbl^(%%c^) / 1024 / 1024 / 1024, 2^) ^& " GiB"
            CScript //NoLogo "%TEMP%\tmp.vbs"
        )
    )
)
type "C:\test\test2.txt"
del "%TEMP%\tmp.vbs"

不过,整个方法还可以改进:

  • 临时 VBScript 脚本可以编写一次,而不是每次 for /F 循环迭代;为此,您需要将变量值作为命令行参数传递;请参阅此线程以了解其工作原理:Using command line arguments in VBscript;
  • 使用echo 编写临时VBScript 文件需要大量转义,例如^&;为了避免这种情况,我们可以将 VBScript 代码放在一个以 :::: 为前缀的块中,这被批处理文件解释器视为无效的跳转标签,甚至可以通过在它们之前放置 exit /B 来跳过它们; findstr可以轻松提取这个块,for /F可以去掉冒号;

这就是我的意思:

> "%TEMP%\tmp.vbs" (for /F "tokens=* delims=:" %%z in ('findstr /B "::::" "%~f0"') do @echo/%%z)
> "C:\test\test2.txt" (
    for /F "skip=1 tokens=1-3" %%a in ('
        wmic LogicalDisk where "DriveType=3" get DeviceID^,FreeSpace^,Size
    ') do @(
        if not "%%c"=="" CScript //NoLogo "%TEMP%\tmp.vbs" "%%a" "%%b" "%%c"
    )
)
type "C:\test\test2.txt"
del "%TEMP%\tmp.vbs"
exit /B

::::If WScript.Arguments.Count < 3 Then WScript.Quit 1
::::WScript.Echo WScript.Arguments.Item(0) & _
::::    " free=" & FormatNumber(CDbl(WScript.Arguments.Item(1)) / 1024 / 1024 / 1024, 2) & " GiB" & _
::::    " size=" & FormatNumber(CDbl(WScript.Arguments.Item(2)) / 1024 / 1024 / 1024, 2) & " GiB"

在应用此线程中演示的技术时,您甚至可以避免使用包含 VBScript 代码的临时文件:Is it possible to embed and execute VBScript within a batch file without using a temporary file?
(另请参阅这些 Microsoft 文章:Using Windows Script Files (.wsf)How Come You Guys Don’t Use .WSF Files?。)

<!-- :Batch script section
> "C:\test\test2.txt" (
    for /F "skip=1 tokens=1-3" %%a in ('
        wmic LogicalDisk where "DriveType=3" get DeviceID^,FreeSpace^,Size
    ') do @(
        if not "%%c"=="" CScript //NoLogo "%~f0?.wsf" "%%a" "%%b" "%%c"
    )
)
type "C:\test\test2.txt"
exit /B

---- WSF script section -->
<job><script language="VBScript">
    If WScript.Arguments.Count < 3 Then WScript.Quit 1
    WScript.Echo WScript.Arguments.Item(0) & _
        " free=" & FormatNumber(CDbl(WScript.Arguments.Item(1)) / 1024 / 1024 / 1024, 2) & " GiB" & _
        " size=" & FormatNumber(CDbl(WScript.Arguments.Item(2)) / 1024 / 1024 / 1024, 2) & " GiB"
</script></job>

这里还有另一种不使用临时 VBScript 文件的方法,应用此线程中说明的方法:HTA & Batch Hybrid, passing variables from BATCH section。这样做的缺点是 HTA 窗口出现和消失的短暂闪烁。
(另请参阅这篇 Microsoft 文章:HTML Applications (HTAs)。)

<!-- ::Batch script section ----
> "C:\test\test2.txt" (
    for /F "skip=1 tokens=1-3" %%a in ('
        wmic LogicalDisk where "DriveType=3" get DeviceID^,FreeSpace^,Size
    ') do @(
        if not "%%c"=="" set "DeviceID=%%a" & set "FreeSpace=%%b" & set "Size=%%c" & MSHTA "%~f0"
    )
)
type "C:\test\test2.txt"
exit /B

---- ::HTA script section -->
<script language="VBScript">
    Set Env = CreateObject("WScript.Shell").Environment("Process")
    Set StdOut = CreateObject("Scripting.FileSystemObject").GetStandardStream(1)
    If Not Env("DeviceID") = "" Then
        StdOut.WriteLine(Env("DeviceID") & _
            " free=" & FormatNumber(CDbl(Env("FreeSpace")) / 1024 / 1024 / 1024, 2) & " GiB" & _
            " size=" & FormatNumber(CDbl(Env("Size")) / 1024 / 1024 / 1024, 2) & " GiB")
    End If
    Set StdOut = Nothing
    Set Env = Nothing
    Close()
</script>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-28
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    • 2013-01-16
    • 2013-07-30
    相关资源
    最近更新 更多