Пакетный скрипт не запускается, хотя его код запускается в CMD - PullRequest
0 голосов
/ 28 июня 2018

Я искал простой способ получить информацию о системных томах в ГБ (пакетном режиме), поэтому получил:

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 голосов
/ 29 июня 2018

В вашем коде есть следующие проблемы:

  • в пакетных файлах, мета-переменным for должны предшествовать два % -символы в отличие от одного в командной строке (cmd), следовательно, изменение %a и т. Д. до %%a и т.д .; в противном случае возникает синтаксическая ошибка;
  • Вы можете избежать фильтрации заголовка вывода wmic на findstr /V при использовании опции skip=1 для for /F;
  • вместо свойства Name Я рекомендую использовать DeviceID, в соответствии с этим потоком Суперпользователя: В чем разница между свойствами Name, Caption и DeviceID (при выполнении wmic LogicalDisk) ;
  • не уверен, какие диски вы хотите запросить, но, возможно, вам нужны только локальные диски; если так, добавьте пункт where "DriveType=3"; обратитесь к этой статье Microsoft: класс Win32_LogicalDisk ;
  • создание временного сценария 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; чтобы это работало, вам нужно передать значения переменных в качестве аргументов командной строки; обратитесь к этой теме, чтобы узнать, как это работает: Использование аргументов командной строки в 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, при применении метода, продемонстрированного в этом потоке: Возможно ли встроить и выполнить VBScript в пакетном файле без использования временного файла?
(См. Также следующие статьи Microsoft: Использование файлов сценариев Windows (.wsf) и Почему вы, ребята, не используете файлы .WSF? .)

<!-- :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, передавая переменные из секции BATCH . Недостатком этого является кратковременное мерцание окон HTA, появляющихся и исчезающих.
(См. Также эту статью Microsoft: Приложения HTML (HTA) .)

<!-- ::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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...