Why is the error message “Missing operand” output

2019-07-25 10:01发布

问题:

I am working on a batch file that is supposed to show the currently estimated charge remaining. But when I run the command I get the desired result plus the error message Missing operand. output.

My code:

FOR /F "delims= skip=1" %%i IN ('WMIC PATH Win32_Battery Get EstimatedChargeRemaining') DO (SET /a CHR=%%i)
ECHO "Battery Level: %CHR%"

How can I get rid of that undesired output Missing operand.?

I have already tried to remove % from the last part, i.e. DO (SET /a CHR=i), but when I do so I end up with "Battery Level: 0" as if it were skipping some of the numbers of the result.

回答1:

The Windows Management Instrumentation Command-line utility outputs text UTF-16 Little Endian encoded with byte order mark (BOM) which means (nearly) always with two bytes per character with not displayed FF FE (BOM as hexadecimal bytes) at beginning of the output text.

FOR is designed for parsing text using ASCII/ANSI/OEM encoding which are character encodings using just one byte per character. The used code page output by running in a command prompt window the command chcp determines which character is represented by a byte with value in decimal range 0 to 255 or hexadecimal 00 to FF. The code page used in Windows command line environment by default depends on which region/country/locale is set for the used user account.

Let us look on posted code:

FOR /F "delims= skip=1" %%i IN ('WMIC PATH Win32_Battery Get EstimatedChargeRemaining') DO (SET /a CHR=%%i)
ECHO "Battery Level: %CHR%"

The output of wmic is for example with representing carriage return + line-feed on Windows 7 and Windows XP:

EstimatedChargeRemaining  ¶
93                        ¶

This output redirected into a file and viewed hexadecimal would look like:

0000h: FF FE 45 00 73 00 74 00 69 00 6D 00 61 00 74 00 ; ÿþE.s.t.i.m.a.t.
0010h: 65 00 64 00 43 00 68 00 61 00 72 00 67 00 65 00 ; e.d.C.h.a.r.g.e.
0020h: 52 00 65 00 6D 00 61 00 69 00 6E 00 69 00 6E 00 ; R.e.m.a.i.n.i.n.
0030h: 67 00 20 00 20 00 0D 00 0A 00 39 00 33 00 20 00 ; g. . .....9.3. .
0040h: 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 ;  . . . . . . . .
0050h: 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 ;  . . . . . . . .
0060h: 20 00 20 00 20 00 20 00 20 00 20 00 20 00 0D 00 ;  . . . . . . ...
0070h: 0A 00                                           ; ..

The format is: hexadecimal offset: hexadecimal bytes ; Windows-1252 character representation

The code above results in skipping the first line output by wmic in a separate command process started by FOR with cmd.exe /C by using environment variable ComSpec in background without displaying an window and captured by FOR containing just the value name EstimatedChargeRemaining.

The second line containing the value of interest with one, two or three digits with 21 to 23 spaces depending on digits of the value is assigned first to loop variable i and next to environment variable CHR using an arithmetic expression to ignore the trailing spaces.

This code written according to a comment by Stephan on your previous question How to get value without unwanted spaces in WMIC results? works perfect on Windows XP, but results on Windows 7 in the error message:

Missing operand.

On Windows XP there is just one loop iteration with assigning the value 93 with the 22 trailing spaces and a carriage return to environment variable CHR whereby the spaces and the carriage return are ignored by command SET because of the arithmetic expression.

But on Windows 7 the loop is run a second time with just a carriage return assigned to loop variable i. The vertical whitespace character carriage return is ignored by command SET on evaluation of the arithmetic expression which results in execution on second loop iteration set /a CHR= and therefore there is indeed an operand after equal sign missing for a syntactically correct arithmetic expression.

There are multiple solutions possible for this issue of FOR on parsing UTF-16 encoded output.

A very easy one is exiting FOR loop after having assigned the value of interest to the environment variable.

for /F "skip=1" %%I in ('%SystemRoot%\System32\wbem\wmic.exe PATH Win32_Battery GET EstimatedChargeRemaining') do set "CHR=%%I" & goto HaveValue
:HaveValue
echo Battery level: %CHR%%%

So the FOR loop iterates just once and processes only the second line of wmic output with the value of interest and the trailing spaces. Option delims= to define an empty list of delimiters is not used to get the trailing spaces already removed by FOR on processing the second line captured from output of wmic and so loop variable I holds the value of interest already without trailing spaces. That makes it possible to omit SET parameter /A to just assign the value to the environment variable without using an arithmetic expression.

Another simple solution to workaround the issue with wrong parsing of UTF-16 encoded line endings of wmic output by FOR is using a code like this:

for /F "tokens=2 delims==" %%I in ('%SystemRoot%\System32\wbem\wmic.exe PATH Win32_Battery GET EstimatedChargeRemaining /VALUE') do set "CHR=%%I"
echo Battery level: %CHR%%%

The output of wmic with option /VALUE is for example with representing carriage return + line-feed:

¶
¶
EstimatedChargeRemaining=93¶
¶
¶

This output redirected into a file and viewed hexadecimal would look like:

0000h: FF FE 0D 00 0A 00 0D 00 0A 00 45 00 73 00 74 00 ; ÿþ........E.s.t.
0010h: 69 00 6D 00 61 00 74 00 65 00 64 00 43 00 68 00 ; i.m.a.t.e.d.C.h.
0020h: 61 00 72 00 67 00 65 00 52 00 65 00 6D 00 61 00 ; a.r.g.e.R.e.m.a.
0030h: 69 00 6E 00 69 00 6E 00 67 00 3D 00 39 00 33 00 ; i.n.i.n.g.=.9.3.
0040h: 0D 00 0A 00 0D 00 0A 00 0D 00 0A 00             ; ............

So there are two empty lines at beginning which are always ignored by FOR even on being UTF-16 encoded. Then there is the line containing value name and the value with an equal sign between. Last there are two more empty lines.

The two FOR options tokens=2 delims== result in splitting the third line up on equal sign with assigning just second string after the equal sign being the value of interest to loop variable I which is simply assigned next to environment variable CHR. The command SET is executed only once because of the wrong parsed empty lines at end of wmic output result only in one substring with just carriage return being found by FOR and so it cannot assign anything to loop variable I as there is no second substring. For that reason FOR does not execute the command SET a second time.

One more simple solution was posted by npocmaka as answer on your previous question by using a second FOR loop to process the line with the value of interest correct.

Other solutions use an additional file or an additional command to output UTF-16 encoded text as ASCII encoded text which FOR can capture and process without any problems. See answers on How to correct variable overwriting misbehavior when parsing output? for such solutions.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • echo /?
  • for /?
  • goto /?
  • set /?
  • wmic /?
  • wmic PATH /?
  • wmic PATH Win32_Battery /?
  • wmic PATH Win32_Battery GET /?


回答2:

It's happening because of the lines which come AFTER the 100 in the WMIC output. WMIC outputs 4 lines for me. It skips the first line, then continues with the others, only one of which works.

@echo off
FOR /F "delims= skip=1" %%i IN ('WMIC PATH Win32_Battery Get EstimatedChargeRemaining') DO (
    SET /a CHR=%%i
    goto loopdone
)
:loopdone
ECHO "Battery Level: %CHR%"