为什么此批处理脚本中的 FOR /f 循环评估空行?

发布于 2024-12-13 18:47:24 字数 768 浏览 1 评论 0原文

我正在尝试编写一个批处理脚本,该脚本获取(除其他外)计算机拥有的所有磁盘驱动器的列表。基本代码如下所示:

REM Build the list of disk drives to monitor
SETLOCAL enabledelayedexpansion
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
    SET "DISK_DATABASES=!DISK_DATABASES!%%a|"
    SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\\|"
)

我显然构建了两个格式略有不同的列表,以供稍后使用。然而,当我运行此命令时,我得到的输出看起来像这样:

C|D|E||
C:\\|D:\\|E:\\|:\\|

现在,我希望在这两种情况下都有尾随管道,并且我可以管理它,但我真的很困惑为什么其中有一个额外的空白条目。如果我手动运行 wmic 命令,我可以看到输出末尾确实有一个空行,但我的理解是 /f 是专门应该的忽略空行。

如果我打开 ECHO ,看起来最后一行只是作为回车符/换行符或类似的内容出现。有办法做我期待的事情吗?我错过了什么吗?我尝试在循环中编写一个 if 条件来排除最后一行,但它......很时髦并且从未起作用。我感谢任何/所有帮助。

I'm trying to write a batch script that obtains (among other things) a list of all of the disk drives the computer has. The basic code looks something like this:

REM Build the list of disk drives to monitor
SETLOCAL enabledelayedexpansion
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
    SET "DISK_DATABASES=!DISK_DATABASES!%%a|"
    SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\\|"
)

I pretty obviously build two lists with slightly different formats for use later. When I run this, however, the output I get looks something like this:

C|D|E||
C:\\|D:\\|E:\\|:\\|

Now, I expect the trailing pipe in both cases and I can manage that, but I'm really confused why there is an extra blank entry in there. If I run the wmic command manually, I can see that there is indeed a blank line at the end of the output, but my understanding is that /f was specifically supposed to ignore blank lines.

If I turn ECHO on, it looks like that last line is just coming in as a carriage return/newline or similar. Is there a way to do what I'm expecting? Am I missing something? I tried to write an if condition in the loop to exclude this last line, but it was... funky and never worked. I appreciate any/all help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

失眠症患者 2024-12-20 18:47:24

我刚过来这个话题。我一直在使用 findstr /v 来排除空行:

FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (

I just came over this topic. I've been using findstr /v to exclude empty lines:

FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (
暮光沉寂 2024-12-20 18:47:24

在这种情况下,最后一次迭代不会生成空项,并且您只能通过 echo %DISK_DATABASES% 获得 C|D|E|| 的输出,
但是 echo !DISK_DATABASES! 会输出 ||D|E|??

这是因为最后一个元素是单个 字符。
并且 字符在百分比扩展后会被直接删除,但不会延迟扩展。

您可以避免这种情况,使用百分比扩展来删除它们

setlocal EnableDelayedExpansion
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
  set "item=%%a"
  call :removeCR

  if not "!item!"=="" (
    SET "DISK_DATABASES=!DISK_DATABASES!!item!|"
    SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!!item!:\\|"
  )
)
goto :eof
:removeCR

:removeCR
set "Item=%Item%"
exit /b

In this case the last iteration produces not an empty item, and you get your output of C|D|E|| only with echo %DISK_DATABASES%,
but echo !DISK_DATABASES! will output ||D|E|??

That's because the last element is a single <CR> character.
And <CR> characters are directly removed after the percent expansion, but not with delayed expansion.

You could avoid this, using the percent expansion to remove them

setlocal EnableDelayedExpansion
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
  set "item=%%a"
  call :removeCR

  if not "!item!"=="" (
    SET "DISK_DATABASES=!DISK_DATABASES!!item!|"
    SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!!item!:\\|"
  )
)
goto :eof
:removeCR

:removeCR
set "Item=%Item%"
exit /b
溺孤伤于心 2024-12-20 18:47:24

根据 http://ss64.com/nt/for_f.html

许多较新的命令和实用程序(例如 WMIC)以 unicode 格式输出文本文件,这些文件无法由需要 ASCII 的 FOR 命令读取。
要转换文件格式,请使用 TYPE 命令。

因此看来 WMIC 和 FOR 不能很好地结合在一起。

According to http://ss64.com/nt/for_f.html

Many of the newer commands and utilities (e.g. WMIC) output text files in unicode format, these cannot be read by the FOR command which expects ASCII.
To convert the file format use the TYPE command.

So it appears that WMIC and FOR don't play nice together.

遮了一弯 2024-12-20 18:47:24

我发现了一种更有效、更可靠的方法来从每行末尾去除不需要的 。没有临时文件,也不需要 CALL。

我不明白 FOR /F 如何将 WMIC unicode 输出转换为 ASCII 的机制。通常 FOR /F 无法读取 unicode。但无论它如何工作,每个转换后的行都以 结尾。 FOR /F 在每个 处换行,然后如果该行的最后一个字符是 ,则会删除最后一个 ;,在本例中留下不需要的

解决方案是简单地将每一行再传递一个 FOR /F :-)

@echo off
setlocal enableDelayedExpansion
for /f "skip=1 delims=" %%A in (
  'wmic logicaldisk where "drivetype=3" get deviceid'
) do for /f "tokens=1 delims=:" %%B in ("%%A") do (
  set "disk_databases=!disk_databases!%%B|"
  set "drives_to_monitor=!drives_to_monitor!%%B:\\|"
)

这种方法比使用正常扩展更可靠,因为您不必担心引用或转义特殊字符。例如,使用正常扩展的 CALL 方法无法处理像 "this & that" & 这样的字符串。另一个。但是这个方法对于这样的字符串是没有问题的。

I discovered a more efficient and more reliable method to strip the unwanted <CR> from the end of each line. No temp file, and no CALL needed.

I don't understand the mechanism of how FOR /F converts the WMIC unicode output into ASCII. Normally FOR /F cannot read unicode. But however it works, each converted line ends with <CR><CR><LF>. FOR /F breaks lines at each <LF>, and then if the last character in the line is <CR> it strips that last <CR>, in this case leaving behind the unwanted <CR>.

The solution is to simply pass each line through one more FOR /F :-)

@echo off
setlocal enableDelayedExpansion
for /f "skip=1 delims=" %%A in (
  'wmic logicaldisk where "drivetype=3" get deviceid'
) do for /f "tokens=1 delims=:" %%B in ("%%A") do (
  set "disk_databases=!disk_databases!%%B|"
  set "drives_to_monitor=!drives_to_monitor!%%B:\\|"
)

This method is more reliable then using normal expansion because you don't have to worry about quoting or escaping special characters. For example, The CALL method that uses normal expansion cannot handle a string like "this & that" & the other. But this method has no problem with such a string.

蓝礼 2024-12-20 18:47:24

添加 <代码>^| findstr . 你只会得到非空行

    REM Build the list of disk drives to monitor
    SETLOCAL enabledelayedexpansion
    FOR /f "skip=1 tokens=1 delims=:" %%a in (
    '"WMIC logicaldisk WHERE drivetype=3 GET deviceid" ^| findstr .') do (
        SET "DISK_DATABASES=!DISK_DATABASES!%%a|"
        SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\|"
    )
    

Add ^| findstr . and you will get only not blank lines

    REM Build the list of disk drives to monitor
    SETLOCAL enabledelayedexpansion
    FOR /f "skip=1 tokens=1 delims=:" %%a in (
    '"WMIC logicaldisk WHERE drivetype=3 GET deviceid" ^| findstr .') do (
        SET "DISK_DATABASES=!DISK_DATABASES!%%a|"
        SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\|"
    )
    

流云如水 2024-12-20 18:47:24

我处理此问题的标准习惯用法是将 WMIC 的输出写入临时文件,然后使用 TYPE(将 UTF16 转换为 ASCII)将其输入 FOR,如下所示:

:: Standard environment setup
setlocal enabledelayedexpansion
:: Every variable whose name starts with "tf" will identify a temporary
:: file - remove any such variables inherited from the parent environment
for /f %%V in ('set tf') do set %%V=
:: Create some temporary filenames. Prefix all of them with this script's
:: own name to avoid clashes with those owned by other scripts.
for /l %%I in (1,1,4) set tf%%I="%temp%\%~n0-temp%%I.txt"

:: Use temp file to work around coding mismatch between WMIC out and FOR in
wmic product where "name like 'Microsoft Office %% 2010'" get packagecache >!tf1!
for /f "skip=1" %%P in ('type !tf1!') do if exist "%%~P" msiexec /x "%%~P" /passive /norestart

:: Before quitting script, clean up temporary files
for /f %%V in ('set tf') do if exist "%%~V" del /f /q "%%~V"
endlocal

My standard idiom for dealing with this is to write the output from WMIC to a temp file, then use TYPE (which reduces UTF16 to ASCII) to feed that into FOR, like this:

:: Standard environment setup
setlocal enabledelayedexpansion
:: Every variable whose name starts with "tf" will identify a temporary
:: file - remove any such variables inherited from the parent environment
for /f %%V in ('set tf') do set %%V=
:: Create some temporary filenames. Prefix all of them with this script's
:: own name to avoid clashes with those owned by other scripts.
for /l %%I in (1,1,4) set tf%%I="%temp%\%~n0-temp%%I.txt"

:: Use temp file to work around coding mismatch between WMIC out and FOR in
wmic product where "name like 'Microsoft Office %% 2010'" get packagecache >!tf1!
for /f "skip=1" %%P in ('type !tf1!') do if exist "%%~P" msiexec /x "%%~P" /passive /norestart

:: Before quitting script, clean up temporary files
for /f %%V in ('set tf') do if exist "%%~V" del /f /q "%%~V"
endlocal
遇到 2024-12-20 18:47:24

运行以下命令:

wmic blah /value | find "=" >> wherever

输出将为:

field=value

请注意,不会有多余的行。

Run the following command:

wmic blah /value | find "=" >> wherever

Output will be:

field=value

Note that there will be no extra lines.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文