Command output set as variable - windows

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%

Related

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.

Batch scripting, mutlple finds and multple do sets

At the moment I am running the following to get some information from 'systeminfo', however it needs to run systeminfo twice which takes some time. How would I be able to do multple 'Find"XXX" do sets'?
For /f "delims=" %%A IN ('systeminfo ^| Find "OS Name"') DO Set "VarA=%%A"
For /f "delims=" %%A IN ('systeminfo ^| Find "BIOS Version"') DO Set "VarB=%%A"
Any help is greatly appreciated.
Next code snippet could work (note set commands are merely ECHOed for debugging purposes; remove capitalized ECHO no sooner than debugged):
For /f "delims=" %%A IN ('systeminfo') DO (
For /F "delims=" %%G IN ('echo %%A ^| Find /I "OS Name"') Do ECHO Set "VarA=%%A"
For /F "delims=" %%G IN ('echo %%A ^| Find /I "BIOS Version"') DO ECHO Set "VarB=%%A"
)
However, follow Stephan's advice and parse wmic output rather (do not forget /value option). To do that correctly, note great Dave Benham's article WMIC and FOR /F: A fix for the trailing <CR> problem
I too would use WMIC, but there is a simple solution if you want to use SYSTEMINFO - use FINDSTR with two /C:"search strings"
for /f "tokens=1,2*" %A in ('systeminfo ^| findstr /c:"OS Name" /c:"BIOS Version"') do set "%A=%C"
The above will define two variables on an English machine: OS and BIOS.
Thanks for the replies guys.
I wanted to set the bit version windows the batch file would run on, I figured it out ages ago but thought I would post it here for those that came across it:
echo %PROCESSOR_ARCHITECTURE% | find "64" > NUL
If %ERRORLEVEL% equ 0 (Set bit=64) else (Set bit=32)
Thanks!

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.

I need a windows command line script that takes an output, edits it and creates an environmental variable

I have Asset tags embedded in BIOS. I use
wmic SYSTEMENCLOSURE get SMBiosAssetTag
This pulls the information I want but it is not formatted well:
SMBIOSAssetTag
11886
I need to just have those 5 numbers and nothing else. I will then use that variable to name the computer with a first logon script. I have spent hours on this, and I could have been done in 3 minutes if this was linux.
Note: I can't put linux tools on these builds :-(
Using
WMIC SYSTEMENCLOSURE GET SMBiosAssetTag /FORMAT:VALUE
will make a better output:
(some empty lines)
SMBIOSAssetTag=CZC1296FLD
(some empty lines)
So, in batch you may just
FOR /F "TOKENS=1,* DELIMS==" %%v IN (WMIC SYSTEMENCLOSURE GET SMBiosAssetTag /FORMAT:VALUE) DO IF /I "%%v" == "SMBIOSAssetTag" SET SMBIOSAssetTag=%%w
Side note: hard part in WMIC output is handling empty lines.
This will work, tested it myself:
for /f "eol=S" %%a in ('wmic SYSTEMENCLOSURE get SMBiosAssetTag^|sort') do (set var=%%a)
It works fine.
Mona
for /f "delims=" %%a in ('wmic SYSTEMENCLOSURE get SMBiosAssetTag') do for /f %%b in ("%%a") do set "var=%%b"
echo %var%

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