批处理脚本无法运行,尽管其代码在CMD中运行 [英] Batch script doesn't run, although its code runs in CMD

查看:87
本文介绍了批处理脚本无法运行,尽管其代码在CMD中运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I was looking for an easy way to get the system volumes info in GB (batch) so I got this:

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文件运行它,它什么也没做.

It works fine if I just copy and paste it to the CMD but if I run it from a .bat file it just don't do anything.

我做错了什么?

PD:我无法使用PowerShell,我需要批量使用它.

PD: I can't use PowerShell, I need it in batch.

推荐答案

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

There are the following issues in your code:

  • 在批处理文件中,for元变量必须以两个 %符号开头,与命令提示符(cmd)中的符号相反,因此更改%a,依此类推.,%%a等;否则会出现语法错误;
  • 使用for /Fskip=1选项时,您可以避免通过findstr /V过滤掉wmic输出的标题;
  • 根据此超级用户线程,我建议使用
  • 而不是属性Name:属性NameCaptionDeviceID(执行wmic LogicalDisk时)有什么区别?;
  • 不确定要查询的驱动器是什么,但是可能只需要本地磁盘;如果是这样,则添加子句where "DriveType=3";请参阅此Microsoft文章:Win32_LogicalDisk类;
  • 创建临时VBScript脚本也应在调用它的if条件内进行,以避免无意义的文件写入活动;
  • 您将在每次循环迭代中追加到文本文件,但是您永远不会在开始时初始化一个空文件.也许这是故意的行为,但我不这么认为;无论如何,您可以为整个for /F循环执行一次重定向,可以根据需要选择(重写)(>)或追加(>>);
  • 进入type命令的管道是没有用的,因为type始终不接受任何传入的数据.我猜想|符号应该已经被&代替了;但是,type命令在循环中没有意义,我认为应该在循环后执行,而不是一次显示完整的收集数据;
  • echo(可能会被删除,因为它只是向控制台写入了空行;
  • 最后应清理临时VBScript文件;
  • in batch files, for meta-variables must be preceded by two %-signs in contrast to one in command prompt (cmd), hence change %a, etc., to %%a, etc.; otherwise, a syntax error arises;
  • you could avoid filtering out the headline of the output of wmic by findstr /V when using the skip=1 option of for /F;
  • instead of the property Name I recommend to use DeviceID, according to this Super User thread: What is the difference between properties Name, Caption and DeviceID (when executing wmic LogicalDisk)?;
  • not sure what drives you want to query, but perhaps you want local disks only; if so, add the clause where "DriveType=3"; refer to this Microsoft article: Win32_LogicalDisk class;
  • creation of the temporary VBScript script should happen inside of the if condition where it is called as well in order to avoid pointless file write activities;
  • you are appending to the text file in every loop iteration, but you never initialise an empty file at the beginning; perhaps this is the intentional behaviour, but I do not think so; anyway, you could do the redirection once for the whole for /F loop, either (over-)writing (>) or appending (>>), as you prefer;
  • the pipe into the type command is useless, because type does not accept any incoming data anyway; I guess the | symbol should have been replaced by &; nevertheless, the type command does not make sense in the loop, I think it should be executed after the loop instead to display the complete collected data once;
  • the echo( could be removed as it just writes a blank line to the console;
  • at the end the temporary VBScript file should be cleaned up;

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

Correcting all these things lead to a batch script like this (I do not write it all in a single line for readability):

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


尽管可以改善整个方法:


The whole approach could be improved though:

  • 临时VBScript脚本可以编写一次,而不是每个for /F循环迭代编写一次;为此,您需要将变量值作为命令行参数传递;请参阅此线程以了解其工作方式:在VBscript中使用命令行参数;
  • 使用echo写入临时VBScript文件需要大量转义,例如,例如^&;为避免这种情况,我们可以将VBScript代码放在以::::为前缀的块中,该文件在批处理文件解释器中被视为无效的跳转标签,甚至可以通过在它们前面放置一个exit /B来跳过它们. ; findstr可以轻松提取此块,而for /F可以除去结肠;
  • the temporary VBScript script could be written once rather than per each for /F loop iteration; for this to work you need to pass the variable values as command line arguments; consult this thread to learn how it works: Using command line arguments in VBscript;
  • using echo to write the temporary VBScript file requires lots of escaping, like ^&, for example; to avoid this, we could place the VBScript code in a block that is prefixed by ::::, which is seen as an invalid jump label by the batch file interpreter, and they could even be skipped by placing an exit /B just before them; this block can easily be extracted by findstr, and the colons can be removed by 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代码的临时文件:是否可以在不使用临时文件的情况下在批处理文件中嵌入和执行VBScript?
(另请参阅这些Microsoft文章:使用Windows脚本文件(.wsf)


You could even avoid a temporary file holding the VBScript code when applying the technique demonstrated in this thread: Is it possible to embed and execute VBScript within a batch file without using a temporary file?
(Refer also to these Microsoft articles: Using Windows Script Files (.wsf), and 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文件的另一种方法,应用此线程中说明的方法:


And here is yet another approach that does not use a temporary VBScript file, applying the method illustrated in this thread: HTA & Batch Hybrid, passing variables from BATCH section. The disadvantage of this is brief flickers of HTA windows appearing and disappearing.
(Refer also to this Microsoft article: 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>

这篇关于批处理脚本无法运行,尽管其代码在CMD中运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆