2011-11-04 7 views
8

Estoy tratando de escribir un script por lotes que obtenga (entre otras cosas) una lista de todas las unidades de disco que tiene la computadora. El código básico se ve más o menos así:¿Por qué el bucle FOR/f en este script por lotes evalúa una línea en blanco?

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

Obviamente, creo dos listas con formatos ligeramente diferentes para su uso posterior. Cuando ejecuto esto, sin embargo, la salida de recibo es como la siguiente:

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

Ahora, espero que el tubo de arrastre en ambos casos y que puedo manejar eso, pero estoy realmente confundido por qué hay un extra entrada en blanco allí. Si ejecuto el comando wmic manualmente, puedo ver que efectivamente hay una línea en blanco al final de la salida, pero mi entendimiento es que se suponía que el /f ignoraba las líneas en blanco.

Si enciendo ECHO, parece que la última línea acaba de entrar como un retorno de carro/línea nueva o similar. ¿Hay alguna manera de hacer lo que estoy esperando? ¿Me estoy perdiendo de algo? Traté de escribir una condición if en el ciclo para excluir esta última línea, pero fue ... funky y nunca funcionó. Agradezco toda ayuda.

Respuesta

5

En este caso, la última iteración no se produce un elemento vacío, y se obtiene la salida de C|D|E|| únicamente con echo %DISK_DATABASES%,
pero echo !DISK_DATABASES! voluntad de salida ||D|E| ??

Esto se debe a que el último elemento es un solo carácter <CR>.
Y los caracteres <CR> se eliminan directamente después de la expansión porcentual, pero no con la expansión demorada.

Usted puede evitar esto, mediante la expansión ciento para eliminarlos

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 
+0

Wow, vivimos y aprendemos, ¿verdad? Me pregunto por qué una utilidad como WMIC produciría un solo carácter '' en una línea en primer lugar ... O tal vez ese es el resultado de lo que @matt descubrió. –

+0

Supongo que no es el problema Unicode, ya que incluso en XP Microsoft no sabía cómo deberían ser sus propias terminaciones de línea. IPconfig (XP) también produce terminaciones de línea inconscientes – jeb

+0

Gracias a jeb y matt por las respuestas. Ambas soluciones funcionaron, pero la de jeb no me requirió crear archivos temporales así que acepto la suya. – Morinar

4

Según http://ss64.com/nt/for_f.html

Muchos de los más nuevos comandos y utilidades (por ejemplo WMIC) de salida de archivos de texto en formato Unicode, éstos no pueden ser leídos por el comando FOR, que espera ASCII. Para convertir el formato de archivo use el comando TYPE.

Parece que WMIC y FOR no funcionan bien juntas.

3

he descubierto un método más eficiente y más fiable para despojar a los no deseados <CR> desde el final de cada línea. No hay archivo temporal y no se necesita CALL.

No entiendo el mecanismo de cómo FOR/F convierte la salida WMIC Unicode en ASCII. Normalmente FOR/F no puede leer unicode. Pero como sea que funcione, cada línea convertida termina con <CR><CR><LF>. FOR/F rompe las líneas en cada <LF>, y luego si el último carácter en la línea es <CR> quita ese último <CR>, en este caso dejando atrás el <CR> no deseado.

La solución es simplemente pasar cada línea a través de una más para/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:\\|" 
) 

Este método es más fiable a continuación, utilizando expansión normal, ya que no tiene que preocuparse por citar o escapar especial caracteres. Por ejemplo, el método CALL que usa expansión normal no puede manejar una cadena como "this & that" & the other. Pero este método no tiene ningún problema con una cadena de este tipo.

+0

Buena idea de usar 'for/f' para esto, normalmente encuentro molesto este comportamiento, pero en este caso es útil. – jeb

0

Mi lenguaje estándar para tratar con esto es escribir la salida de WMIC a un archivo temporal, a continuación, utilizar el tipo (que reduce UTF16 a ASCII) para alimentar a que en A, así:

:: 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 
0

Run el siguiente comando:

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

salida será:

field=value 

Tenga en cuenta que no habrá adicional líneas.

11

Acabo de hablar de este tema. He estado usando findstr/v para excluir líneas vacías:

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

Esta es una respuesta mucho mejor a IMO, no requiere expansión retrasada,: CALL o anidados para bucles. Fácil y simple! – wpg4665

+0

mucho mejor de hecho, thx dmitry – Deus777

0

Añadir ^| findstr . y obtendrá sólo líneas en blanco no

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

Cuestiones relacionadas