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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8015489/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 18:24:08  来源:igfitidea点击:

Why is the FOR /f loop in this batch script evaluating a blank line?

windowsbatch-filecmdwmic

提问by Morinar

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 wmiccommand manually, I can see that there is indeed a blank line at the end of the output, but my understanding is that /fwas specifically supposed to ignore blank lines.

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

If I turn ECHOon, 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 ifcondition in the loop to exclude this last line, but it was... funky and never worked. I appreciate any/all help.

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

采纳答案by jeb

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

在这种情况下,最后一次迭代产生的不是空项目,您只得到C|D|E||with的输出echo %DISK_DATABASES%
echo !DISK_DATABASES!会输出||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.

那是因为最后一个元素是单个 <CR>字符。
并且<CR>字符在百分比扩展后被直接删除,而不是延迟扩展。

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

回答by dmitry

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

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

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

回答by matt

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

根据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.

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

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

所以看起来 WMIC 和 FOR 不能很好地一起玩。

回答by dbenham

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.

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

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

我不明白 FOR /F 如何将 WMIC unicode 输出转换为 ASCII 的机制。通常 FOR /F 无法读取 unicode。但是无论它如何工作,每个转换后的行都以<CR><CR><LF>. FOR /F 在每一行断行<LF>,然后如果行中的最后一个字符是<CR>它去掉最后一个<CR>,在这种情况下留下不需要的<CR>

The solution is to simply pass each line through one more 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:\|"
)

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.

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

回答by flabdablet

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:

我处理这个问题的标准习惯用法是将 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

回答by David Lang

Run the following command:

运行以下命令:

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

Output will be:

输出将是:

field=value

Note that there will be no extra lines.

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

回答by André Pires

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

添加^| 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:\|"
    )