Pipe symbol | unexpected in FOR /F loop - windows

FOR /F "tokens=3 delims= " %i IN (query session | FINDSTR /i "console") DO set "ID=%i"
I am getting an error | was unexpected at this time.

Two very simple mistakes to avoid.
First by reading For /? use 'single quotes' for commands.
Secondly ^escape the |
FOR /F "tokens=3 delims= " %i IN ('query session ^| FINDSTR /i "console"') DO set "ID=%i"
Reminder as mentioned by #aschipfl in comment use doubled %% for both of your i's in a batch file or file.cmd

You should always read the usage information for a command utility before you use it. Had you done so, you would have noted that as you are specifically trying to isolate a line with the session name console, you could have used query session console instead of the less robust query session | FINDSTR /i "console". Of course, using the more appropriate command would mean that you do not have any issue with a horizontal bar, (pipe).
If you wanted, you could also skip the first, (header), line, and if you are certain, that your ID will always be the third whitespace separated token, you could then use:
From the Command Prompt, (cmd):
For /F "Skip=1 Tokens=3" %G In ('%SystemRoot%\System32\query.exe Session Console 2^>NUL') Do #Set "ID=%G"
Or from a batch file, (batch-file):
#For /F "Skip=1 Tokens=3" %%G In ('%SystemRoot%\System32\query.exe Session Console 2^>NUL') Do #Set "ID=%%G"

Related

Command output set as variable

I've been trying to make a script that installs the current nvidia driver, I've gone pretty far but there's one thing missing
I'm trying to use nvidia-smi to find the driver version and here's the command output
C:\>nvidia-smi --query-gpu=driver_version --format=csv
driver_version
457.30
I've been trying to set 457.30 in %driver% here's what I got so far
FOR /F "tokens=* skip=1" %%g IN ('nvidia-smi --query-gpu=driver_version --format=csv') do (SET "driver=%%g")
I also tried a combination with findstr but that ended up being a disaster
for /F "tokens=* skip=1" %%g in ('nvidia-smi --query-gpu=driver_version --format=csv ^| findstr "."') do set driver=%%g
In any case, %%g and %driver% return as empty.
echo %driver%
returns
C:\>echo
ECHO is on.
Any ideas?
Thank you for your cooperation.
Your variable isn't getting set because right now your nvidia-smi command is throwing an error (to stdout, curiously) but skip=1 is skipping over it so there's nothing left to set the variable to.
= is one of the default delimiters for strings and so both of the = symbols in your command need to be escaped for your query to be executed correctly.
#echo off
for /F "delims=" %%g IN ('nvidia-smi --query-gpu^=driver_version --format^=csv ^| find "."') do set "driver=%%g"
echo %driver%

Quoting a long filenamed command in a for loop in a batch file

Linked:
Best free resource for learning advanced batch-file usage?
Dealing with quotes in Windows batch scripts
This appears to be one of those maddening quoting issues. In this example program:
#echo off
set wmicpath=%windir%\System32\wbem\wmic.exe
for /f "usebackq" %%a in (`%wmicpath% COMPUTERSYSTEM GET SystemType ^| findstr /I "x64"`) do (
echo %%a
)
The program runs just fine. Unless you try to quote the wmicpath. Imagine if you will that it contains a long path name. Then you should quote it. But I cannot quite get it to work. This fails:
for /f "usebackq" %%a in (`"%wmicpath%" COMPUTERSYSTEM GET SystemType ^| findstr /I "x64"`) do (
but this works!:
for /f "usebackq" %%a in (`"%wmicpath%" COMPUTERSYSTEM GET SystemType ^| findstr /I x64`) do (
as does this:
for /f "usebackq" %%a in (`"%wmicpath%" COMPUTERSYSTEM GET SystemType`) do (
There's something really odd about matching quotes in a for command. You can quote a command as long as you don't start quoting elsewhere...
Is it possible? I tried escaping at various points but I'm not sure about the escaping rules when quotes are involved...
Edit: I think this link might be the issue (ie: it's a bug): Pipe in for loop breaks double quoted variables
#echo off
setlocal enableextensions disabledelayedexpansion
set "wmicpath=%windir%\System32\wbem\wmic.exe"
for /f "usebackq delims=" %%a in (`
^""%wmicpath%" COMPUTERSYSTEM GET SystemType ^| findstr /I "x64"^"
`) do (
echo %%a
)
If you look at the start and end of the inner command, you will see two additional ^" (a escaped double quote). Your problem is that the for command is spawning a separate instance of cmd to handle the inner command, and this separate instance is removing the initial and final double quotes.
Why escaped quotes? To avoid this additional quotes being paired with the double quotes in the command that could lead to some other parsing problems.
You can run cmd /? to obtain the help page (sorry, i have a spanish locale so i will not include the output here). You will see a section about the /C and /K usage explaining quote removal behaviour.
First of all I would change the command, WMIC allows you to use a query language LIKE operator which would in this case remove the need to pipe anything.
#Echo Off
Set "WMIC=%SystemRoot%\System32\Wbem\wmic.exe"
For /F "UseBackQ Skip=1" %%a In (
`""%WMIC%" ComputerSystem Where "SystemType Like 'x64%%'" Get SystemType"`
) Do For %%b In (%%a) Do Echo=%%b
Timeout -1
Then I may even change the format of the command such that I don't use back quotes.
#Echo Off
Set "WMIC=%SystemRoot%\System32\Wbem\wmic.exe"
For /F "Skip=1" %%a In (
'""%WMIC%" ComputerSystem Where (SystemType Like "x64%%") Get SystemType"'
) Do For %%b In (%%a) Do Echo=%%b
Timeout -1
Whilst this doesn't directly answer the question in the subject title, it does allow for your particular command to work correctly.
However neither are necessary to your particular command example, because you do not need the for loop to echo that output:
#Echo Off
Set "WMIC=%SystemRoot%\System32\Wbem\wmic.exe"
"%WMIC%" ComputerSystem Get SystemType|Find /I "x64"
Timeout -1
Change Find to FindStr if you feel the need.
>x64.txt ECHO x64
for /f "usebackq" %%a in (`"%wmicpath%" COMPUTERSYSTEM GET SystemType ^| findstr /I /g:x64.txt`) do (
might be a work-around, depending on your actual application and preferences.

Echo Without Newline in a Windows Batch `for` Loop

Please note: This question is not about how to echo without a newline. It's about to pipe a variable without a newline and store the result in another variable. Please don't mark this question as duplicate to other questions answering only how to remove newlines!
I have a variable a which I want to pass to a program (let's use more for the sake of this example), and its result should be stored into another variable b. This is to prevent the creation of temporary files. Sticking some answers from other questions here together, this could be achieved by something like this:
FOR /f "tokens=*" %%t IN ('ECHO %a% ^| MORE') DO SET b=%%t
Which works -- BUT will add another newline on my variable! So I tried the following tricks you find on stackoverflow/superuser to prevent the newline. If you do these without the loop, they work perfectly! But once you put them in a loop, they fail:
FOR /f "tokens=*" %%t IN ('ECHO ^| SET /p="%a%" ^| MORE') DO SET b=%%t
FOR /f "tokens=*" %%t IN ('^<NUL SET /p="%a%" ^| MORE') DO SET b=%%t
It will always say The syntax of the command is incorrect. What am I missing? Please help me!
If you run your command
FOR /f "delims=" %%t IN ('^<NUL SET /p="%a%" ^| more ') DO SET "b=%%t"
with echo ON you will see the equal sign in the set command has vanished, it has been removed by the parser leaving an incorrect command.
You have two options:
FOR /f "delims=" %%t IN ('^<NUL SET /p^="%a%" ^| more ') DO SET "b=%%t"
FOR /f "delims=" %%t IN ('^<NUL SET /p"=%a%" ^| more ') DO SET "b=%%t"
The first one escapes the equal sign. The second one quotes it.

'cut' like feature for Windows batch file

It's been a while since I've done Windows batch files, and I seem to have forgotten everything. What I want to do is look for services where the path has spaces but the string isn't quoted. Boy, this would be easy with bash, but...
So, in a nutshell, I start with sc query | findstr SERVICE_NAME and dump that to a temp file. Then I read it back in to a variable with for /F "tokens=2" %%f in (temp_file) do set services=!services! %%f That gets me a variable with a space-delimited list of all services on my host. Now, in a FOR loop against that variable, I run sc qc %%s | findstr BINARY_PATH_NAME and dump THAT to a temp file. It winds up containing something like (quotes are mine to preserve all the spaces):
" BINARY_PATH_NAME : C:\Windows\system32\svchost.exe -k LocalSystemNetworkRestricted"
I've been reading and Googling and testing and trying everything, trying to wind up cutting that output at the : so I can just wind up with the path itself, and then start figuring out the abomination that must be regular expressions under Windows. But I just cannot get that string split.
You can do the following if you want the binary path for all services:
for /f "tokens=2" %%n in ('sc query ^| findstr SERVICE_NAME') do (
for /f "delims=: tokens=1*" %%r in (
'sc qc "%%~n" ^| findstr BINARY_PATH_NAME'
) do (
echo %%~s
)
)
Change echo %%~s to echo %%~n:%%~s if you want the binary path prepended with the name of the service.
Ansgar Wiechers has a good solution using SC to get the info for running services. It also demonstrates how to use the FOR /F delims option to break at the :.
Another option is to use WMIC to get the same information in a more direct manner.
If all you want is a list of binary paths for all active (running) services, then all you need is:
wmic service where "state='Running'" get pathname
If you want the list of service names as well as the binary paths:
wmic service where "state='Running'" get name, pathname
There are many more properties that can be listed. Type wmic service get /? from the command prompt to get a complete list.
If you want to get the values into variables within a batch process so that you can take action, then a FOR /F loop is used. I append the state property at the end to avoid an odd FOR /F quirk that appends an unwanted <CR> to the end of each line of WMIC output. The unwanted <CR> will be attached to the state value, which we don't care about. I also use the state with FINDSTR to weed out unwanted lines, so there is no need for the WMIC WHERE clause. In this example I simply echo the values, but obviously you could do whatever is needed with them.
for /f "tokens=2,3 delims=," %%A in (
'"wmic service get name, pathname, state /format:csv|findstr /e Running"'
) do echo %%A: %%B
Something like this?
#echo off
for /f "tokens=2 delims=: " %%a in ('sc query ^| findstr SERVICE_NAME') do (
sc qc %%a | findstr BINARY_PATH_NAME
)
pause

Why is the FOR /f loop in this batch script evaluating a blank line?

I'm trying to write a batch script that obtains (among other things) a list of all of the disk drives the computer has. The basic code looks something like this:
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:\\|"
)
I pretty obviously build two lists with slightly different formats for use later. When I run this, however, the output I get looks something like this:
C|D|E||
C:\\|D:\\|E:\\|:\\|
Now, I expect the trailing pipe in both cases and I can manage that, but I'm really confused why there is an extra blank entry in there. If I run the wmic command manually, I can see that there is indeed a blank line at the end of the output, but my understanding is that /f was specifically supposed to ignore blank lines.
If I turn ECHO on, it looks like that last line is just coming in as a carriage return/newline or similar. Is there a way to do what I'm expecting? Am I missing something? I tried to write an if condition in the loop to exclude this last line, but it was... funky and never worked. I appreciate any/all help.
I just came over this topic. I've been using findstr /v to exclude empty lines:
FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (
In this case the last iteration produces not an empty item, and you get your output of C|D|E|| only with echo %DISK_DATABASES%,
but echo !DISK_DATABASES! will output ||D|E|??
That's because the last element is a single <CR> character.
And <CR> characters are directly removed after the percent expansion, but not with delayed expansion.
You could avoid this, using the percent expansion to remove them
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
According to http://ss64.com/nt/for_f.html
Many of the newer commands and utilities (e.g. WMIC) output text files in unicode format, these cannot be read by the FOR command which expects ASCII.
To convert the file format use the TYPE command.
So it appears that WMIC and FOR don't play nice together.
I discovered a more efficient and more reliable method to strip the unwanted <CR> from the end of each line. No temp file, and no CALL needed.
I don't understand the mechanism of how FOR /F converts the WMIC unicode output into ASCII. Normally FOR /F cannot read unicode. But however it works, each converted line ends with <CR><CR><LF>. FOR /F breaks lines at each <LF>, and then if the last character in the line is <CR> it strips that last <CR>, in this case leaving behind the unwanted <CR>.
The solution is to simply pass each line through one more 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:\\|"
)
This method is more reliable then using normal expansion because you don't have to worry about quoting or escaping special characters. For example, The CALL method that uses normal expansion cannot handle a string like "this & that" & the other. But this method has no problem with such a string.
Add ^| findstr . and you will get only not blank lines
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:\|"
)
My standard idiom for dealing with this is to write the output from WMIC to a temp file, then use TYPE (which reduces UTF16 to ASCII) to feed that into FOR, like this:
:: 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
Run the following command:
wmic blah /value | find "=" >> wherever
Output will be:
field=value
Note that there will be no extra lines.

Resources