2011-11-04 16 views
8

Sto provando a scrivere uno script batch che ottiene (tra le altre cose) un elenco di tutte le unità disco del computer. Il codice di base simile a questa:Perché il ciclo FOR/f in questo script batch valuta una riga vuota?

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

ho abbastanza, ovviamente, costruire due liste con formati leggermente diversi per un uso successivo. Quando ho eseguito questo, però, l'uscita ottengo simile a questa:

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

Ora, mi aspetto che il tubo finale in entrambi i casi e posso gestire tale, ma sono davvero confusa perché c'è un extra entrata vuota lì. Se eseguo il comando wmic manualmente, posso vedere che c'è effettivamente una riga vuota alla fine dell'output, ma la mia comprensione è che lo /f doveva ignorare le righe vuote.

Se accendo lo ECHO, sembra che l'ultima riga sia appena arrivata come un ritorno a capo/a capo o simile. C'è un modo per fare ciò che mi aspetto? Mi sto perdendo qualcosa? Ho provato a scrivere una condizione if nel ciclo per escludere quest'ultima riga, ma era ... funky e non ha mai funzionato. Apprezzo qualsiasi/tutto l'aiuto.

risposta

5

In questo caso l'ultima iterazione non produce un elemento vuoto, e si ottiene l'output di C|D|E|| soltanto con echo %DISK_DATABASES%,
ma echo !DISK_DATABASES! volontà uscita ||D|E| ??

Questo perché l'ultimo elemento è un singolo carattere <CR>.
E i caratteri <CR> vengono rimossi direttamente dopo l'espansione percentuale, ma non con l'espansione ritardata.

si potrebbe evitare questo, utilizzando l'espansione per cento per rimuoverli

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, viviamo e imparare, non è vero? Mi chiedo perché una utility come WMIC produca un singolo carattere '' su una riga in primo luogo ... O forse questo è il risultato di ciò che @matt ha scoperto. –

+0

Suppongo che non sia il problema Unicode, in quanto anche in XP Microsoft non sapeva come dovrebbero essere i loro finali di linea. IPconfig (XP) produce anche terminazioni di linea incostanti – jeb

+0

Grazie a jeb e matt per le risposte. Entrambe le soluzioni hanno funzionato, ma jeb non mi ha richiesto di creare file temporanei, quindi accetto il suo. – Morinar

4

Secondo http://ss64.com/nt/for_f.html

Molti dei comandi più recenti e le utility (ad esempio WMIC) i file di testo di output in formato Unicode, questi non può essere letto dal comando per il quale si aspetta ASCII. Per convertire il formato file, utilizzare il comando TYPE.

Quindi sembra che WMIC e FOR non suonino bene insieme.

3

ho scoperto un metodo più efficiente e più affidabile per mettere a nudo gli indesiderati <CR> dalla fine di ogni riga. Nessun file temporaneo e nessuna CHIAMATA necessaria.

Non capisco il meccanismo di come FOR/F converte l'output Unicode WMIC in ASCII. Normalmente FOR/F non può leggere unicode. Ma comunque funziona, ogni linea convertita finisce con <CR><CR><LF>. FOR/F interrompe le linee in corrispondenza di ogni <LF>, quindi se l'ultimo carattere nella riga è <CR>, esso rimuove l'ultimo <CR>, in questo caso lasciando dietro di sé lo <CR> indesiderato.

La soluzione è quello di passare semplicemente attraverso ogni linea uno in più per/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:\\|" 
) 

Questo metodo è più affidabile quindi utilizzando l'espansione normale, perché non dovete preoccuparvi di citare o fuggire speciale personaggi. Ad esempio, il metodo CALL che utilizza l'espansione normale non può gestire una stringa come "this & that" & the other. Ma questo metodo non ha problemi con una stringa simile.

+0

Bella idea di usare 'for/f' per questo, normalmente ho trovato questo comportamento fastidioso, ma in questo caso è utile. – jeb

0

mio linguaggio standard per affrontare questo è quello di scrivere l'output di WMIC in un file temporaneo, quindi utilizzare TIPO (che riduce UTF16 a ASCII) per alimentare che in PER, in questo modo:

:: 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 il seguente comando:

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

uscita sarà:

field=value 

si noti che non ci saranno più Linee.

11

Sono appena arrivato su questo argomento. Sto usando findstr/v di escludere le righe vuote:

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

Questa è una risposta molto migliore IMO, non richiede un'espansione ritardata,: CALL o nidificati per cicli. Facile e semplice! – wpg4665

+0

molto meglio, grazie dmitry – Deus777

0

Aggiungere ^| findstr . e si otterrà solo non righe vuote

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

Problemi correlati