Почему цикл FOR / f в этом пакетном скрипте оценивает пустую строку? - PullRequest
8 голосов
/ 05 ноября 2011

Я пытаюсь написать пакетный скрипт, который получает (среди прочего) список всех дисководов, которые есть у компьютера.Базовый код выглядит примерно так:

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 в цикле, чтобы исключить эту последнюю строку, но это было ... забавно и никогда не работало.Я ценю любую помощь.

Ответы [ 7 ]

11 голосов
/ 18 июня 2014

Я только что подошел к этой теме. Я использовал findstr / v для исключения пустых строк:

FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (
5 голосов
/ 05 ноября 2011

В этом случае последняя итерация создает не пустой элемент, и вы получаете вывод C|D|E|| только с echo %DISK_DATABASES%,
но echo !DISK_DATABASES! выведет ||D|E| ??

Это потому, что последний элемент представляет собой один <CR> символ.
И <CR> символы удаляются непосредственно после расширения в процентах, но не с отложенным расширением.

Вы можете избежать этого, используя расширение процента, чтобы удалить их

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
4 голосов
/ 05 ноября 2011

Согласно http://ss64.com/nt/for_f.html

Многие из более новых команд и утилит (например, WMIC) выводят текстовые файлы в формате Unicode, они не могут быть прочитаны командой FOR, которая ожидает ASCII.Чтобы преобразовать формат файла, используйте команду TYPE.

Так что получается, что WMIC и FOR не очень хорошо играют вместе.

3 голосов
/ 02 февраля 2013

Я обнаружил более эффективный и более надежный способ удаления нежелательных <CR> с конца каждой строки. Нет временного файла и не требуется CALL.

Я не понимаю, как FOR / F преобразует вывод Unicode WMIC в ASCII. Обычно FOR / F не может читать Unicode. Но как бы это ни работало, каждая преобразованная строка заканчивается <CR><CR><LF>. FOR / F разбивает строки на каждом <LF>, а затем, если последний символ в строке равен <CR>, он удаляет этот последний <CR>, в этом случае оставляя нежелательные <CR>.

Решение состоит в том, чтобы просто пропустить каждую строку через еще один 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" & the other. Но у этого метода нет проблем с такой строкой.

0 голосов
/ 11 января 2017

Добавьте ^| 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:\|"
    )
    
0 голосов
/ 12 июня 2013

Запустите следующую команду:

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

Вывод будет:

field=value

Обратите внимание, что лишних строк не будет.

0 голосов
/ 19 мая 2013

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