批处理脚本无法运行,尽管其代码在CMD中运行

时间:2018-06-28 19:45:15

标签: batch-file cmd

我一直在寻找一种以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,我需要批量使用它。

1 个答案:

答案 0 :(得分:1)

您的代码中存在以下问题:

  • 在批处理文件中,for元变量必须以 2 %-符号开头,而不是命令提示符(cmd)中的符号。将%a等更改为%%a等;否则会出现语法错误;
  • 使用wmic的{​​{1}}选项时,您可以避免用findstr /V过滤掉skip=1的输出标题;
  • 根据以下超级用户线程,我建议使用for /F而不是属性NameWhat is the difference between properties Name, Caption and DeviceID (when executing wmic LogicalDisk)?;
  • 不确定要查询的驱动器是什么,但是可能只需要本地磁盘;如果是这样,请添加子句DeviceID;请参阅此Microsoft文章:Win32_LogicalDisk class;
  • 创建临时VBScript脚本也应在where "DriveType=3"条件内进行,以免进行无意义的文件写入活动;
  • 您将在每次循环迭代中将其追加到文本文件,但是您永远不会在开始时初始化一个空文件。也许这是故意的行为,但我不这么认为;无论如何,您可以为整个if循环执行一次重定向,可以根据需要(重写)(for /F)或追加(>);
  • 进入>>命令的管道是没有用的,因为type仍然不接受任何传入的数据;我猜应该将type符号替换为|;尽管如此,&命令在循环中没有意义,我认为应该在循环后执行,而不是一次显示完整收集的数据;
  • type可能会被删除,因为它只是向控制台写入了空行;
  • 最后,应清理临时VBScript文件;

更正所有这些内容会导致这样的批处理脚本(为了可读性,我不会将它们全部写在一行中):

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" 循环迭代编写一次;为此,您需要将变量值作为命令行参数传递;请参阅此线程以了解其工作原理:Using command line arguments in VBscript;
  • 使用for /F写入临时VBScript文件需要大量转义,例如echo;为避免这种情况,我们可以将VBScript代码放在以^&为前缀的块中,批处理文件解释器将其视为无效的跳转标签,甚至可以通过放置{{1}来跳过它们}就在他们面前; ::::可以轻松提取该块,exit /B可以除去冒号;

这就是我的意思:

findstr

在应用此线程中演示的技术时,您甚至可以避免保存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?。)

for /F

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

> "%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"