Pourquoi la boucle FOR /f dans ce script batch évalue-t-elle une ligne vierge?
je suis en train d'écrire un script batch qui obtient (entre autres choses) une liste de tous les disques durs de l'ordinateur. Le code de base ressemble à ceci:
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:|"
)
je construis évidemment assez deux listes avec des formats légèrement différents pour une utilisation ultérieure. Quand j'exécute ceci, cependant, la sortie que je reçois ressemble à quelque chose comme ceci:
C|D|E||
C:|D:|E:|:|
maintenant, je m'attends au tuyau arrière dans les deux cas et je peux y arriver, mais je suis vraiment confondu pourquoi il y a un extra vierge d'entrée. Si j'exécute la commande wmic
manuellement, je peux voir qu'il y a effectivement une ligne vide à la fin de la sortie, mais ma compréhension est que /f
était spécifiquement supposé ignorer les lignes vides.
si j'allume ECHO
, on dirait que cette dernière ligne vient juste d'arriver comme retour de chariot/newline ou similaire. Est-il un moyen de faire ce que j'attends? Ai-je raté quelque chose? J'ai essayé d'écrire un if
condition dans la boucle pour exclure cette dernière ligne, mais il était... funky et n'a jamais fonctionné. J'apprécie tous les/tous les aider.
7 réponses
dans ce cas, la dernière itération ne produit pas un article vide, et vous obtenez votre sortie de C|D|E||
seulement avec echo %DISK_DATABASES%
,
mais echo !DISK_DATABASES!
sortira ||D|E|
??
c'est parce que le dernier élément est un seul caractère <CR>
.
Et les caractères <CR>
sont directement supprimés après l'expansion en pourcentage, mais pas avec l'expansion retardée.
vous pourriez éviter cela, en utilisant le pourcentage d'expansion pour les supprimer
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
je viens d'aborder ce sujet. J'ai utilisé findstr /v pour exclure les lignes vides:
FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (
selon http://ss64.com/nt/for_f.html
plusieurs des nouvelles commandes et des utilitaires (par exemple WMIC) fichiers texte de sortie au format unicode, ceux-ci ne peuvent pas être lus par la commande FOR qui attend ASCII. Pour convertir le format de fichier, utilisez la commande TYPE.
il semble donc que WMIC et FOR ne jouent pas bien ensemble.
j'ai découvert une méthode plus efficace et plus fiable pour enlever le <CR>
indésirable à partir de l'extrémité de chaque ligne. Pas de fichier temporaire, et pas d'appel nécessaire.
Je ne comprends pas comment FOR /f convertit la sortie WMIC unicode en ASCII. Normalement FOR / F ne peut pas lire unicode. Mais peu importe comment cela fonctionne, chaque ligne convertie se termine par <CR><CR><LF>
. FOR / F casse les lignes à chaque <LF>
, et puis si le dernier caractère de la ligne est <CR>
il bandes qui durent <CR>
, dans ce cas laissant derrière le indésirable <CR>
.
la solution est de simplement passer chaque ligne à travers une autre pour / 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:\|"
)
cette méthode est plus fiable que d'utiliser l'extension normale car vous n'avez pas à vous soucier de citer ou d'échapper des caractères spéciaux. Par exemple, la méthode D'appel qui utilise l'expansion normale ne peut pas gérer une chaîne comme "this & that" & the other
. Mais cette méthode n'a aucun problème avec une chaîne.
mon idiome standard pour faire face à cela est d'écrire la sortie de WMIC à un fichier temp, puis utiliser le TYPE (qui réduit UTF16 à ASCII) pour alimenter que dans POUR, comme ceci:
:: 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
exécuter la commande suivante:
wmic blah /value | find "=" >> wherever
sortie sera:
field=value
Notez qu'il n'y aura pas de lignes supplémentaires.
Ajouter ^| findstr .
et vous obtiendrez non seulement des lignes vides
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:\|" )