В вашем коде есть следующие проблемы:
- в пакетных файлах, мета-переменным
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>