I have a task to query some hostnames for their serial number stored in the BIOS. I have the hostnames in a text file gets processed with a for loop, the clients are queried for the serial number with another for loop, and the result is directed into another text file.
This works no problem if the host is offline or doesn't respond. The hostname is written to the text file and the serial number is blank, and the script then moves on to the next entry in the input file. But if the host responds and a serial number is stored in the variable, the script initially writes to the output file as it should, but then it writes another line with the same hostname and a blank serial number before moving onto the next line in the input file.
#ECHO OFF
set list=c:\temp\input.txt
for /F %%C in (%list%) do (
for /F "skip=1" %%S in ('wmic /node:%%C bios get serialnumber') do (
echo %%C - %%S >>c:\temp\output.txt)
)
pause
This results in output that looks like this:
DC6068WA00829 -
DC6054WA00178 -
DC6061WA00065 - R93NSI3
DC6061WA00065 -
DC6061WA00064 - R0KBN3S
DC6061WA00064 -
DC6023LA034284 -
DC6038LA034272 -
As you can see, when the host doesn't respond because it's offline or can't be contacted for whatever reason, it moves on to the next one just as it should. But when one responds, it immediately writes another line with just the hostname and a dash.
I've tried making the write command to the output file as a separate subroutine call, I've tried removing the writing of the %%S variable to see if that was interfering in some way, I've tried saving the results to different variables before outputting them to the file, but nothing makes any difference.
I can clean up the resulting file without too much issue. I mostly just want to know what I'm missing that would be causing this to happen.
Any help or insight would be appreciated.
You will notice that when you run by itself:
wmic /node:somehostname bios get serialnumber
It still has Unicode cr/lf characters, which you cannot see on cmd prompt terminal as they are displayed as empty lines. This however causes the characters to be assigned to the %%C meta variable and run the loop again on these characters, will display both the entries in your file.. Instead, we check if a variable exists and create it if not, which will ensure we only assign the actual value. Note, we are using delayedexpansion here as we are setting and using variables inside of a code block.
#echo off
setlocal enabledelayedexpansion
set "list=C:\temp\input.txt"
type nul>"C:\Temp\output.txt"
for /F %%C in (%list%) do (
for /F "skip=1" %%S in ('wmic /node:%%C bios get serialnumber') do if not defined serial set "serial=%%C - %%S"
echo(!serial!>>"C:\temp\output.txt"
)
type output.txt
pause
Note
I created type nul>"C:\Temp\output.txt" to ensure we do not append to previous runs of the script, if you want it to append, then remove that line.
wmic.exe output is crippled with the unusual line ending cr/cr/lf,
resulting from an erroneous unicode conversion.
for /f would skip empty lines, but can't with those cr/cr/lf sequences
so these lines with just the %%C are output.
one solution is another for parsing the uppercase %%S with %%s
technically all the for /f are one line:
:: Q:\Test\2019\04\23\SO_55804785.cmd
#ECHO OFF
set "FileIn=c:\temp\input.txt"
set "FileOut=c:\temp\output.txt"
for /F "usebackq delims=" %%C in (
"%FileIn%"
) do for /F "skip=1 delims=" %%S in (
'wmic /node:%%C bios get serialnumber 2^>Nul ^|findstr "^." '
) do for /F "delims=" %%s in (
"%%S"
) do echo %%C - %%s >>"%FileOut%"
pause
Related
i currently have this command for a batch file
for /F "skip=1 tokens=1 delims=\n" %i in (stats.txt) do echo %i
with the contents of stats.txt being
Title = Subaru's Great Rehab Strategy
URL = http://dynasty-scans.com/chapters/subarus_great_rehab_strategy
Tags = Subaru x Tsukasa[|]Yuri[|]
No. of Pages = 3
^ NOTE: the final line is actually blank
the idea of the line of code is to return the 2nd line with URL. the end goal would be that i would run this line in some sort of loop going though a series of ~12000+ stats.txt files and collecting all the URL lines into a single file
but when i run the command i get this
as you can see it has skipped the first line but it's cutting off where the n in dynasty and outputting the last 3 lines.
now if i remove delims=\n i get the same 3 lines but i don't get the first word before the space which seems to indicate that the value of delims is what splits a line into "tokens" which then i just grab the first one (and space must be the default)
when i go into notepad++, open the Find and Replace Dialog, turn Search Mode to extended and look for "\r\n" i get taken to the end of each line which is why i chose delims to be \n assuming this would then make the entire line one token
So my question is How can i get all of the 2nd line only of my stats.txt file?
The for /f loop already treats the carriage return and / or line feed as an end-of-line. No need to specify it as a delimiter. With delims=\n you're actually saying that all literal backslashes and letter n's should be treated as token delimiters. If you want the whole line, what you want is "skip=1 delims=".
Just out of habit, when reading the contents of a file with a for /f loop, I find it useful to enable usebackq just in case the filename / path contains a space or ampersand. That allows you to quote the filename to protect against such potential treachery.
#echo off
setlocal
for /F "usebackq skip=1 delims=" %%I in ("stats.txt") do if not defined URL set "URL=%%~I"
echo %URL%
Put into context, to use this to read many files named stats.txt and output the URLs into a single collection, enclose the whole thing in another for loop and enable delayed expansion.
#echo off
setlocal
>URLs.txt (
for /R %%N in ("*stats.txt") do (
for /F "usebackq skip=1 delims=" %%I in ("%%~fN") do (
if not defined URL set "URL=%%~I"
)
setlocal enabledelayedexpansion
echo(!URL!
endlocal
set "URL="
)
)
echo Done. The results are in URLs.txt.
If you want to strip the "URL = " from the beginning of each line and keep only the address, you could try changing your for /F parameters to "usebackq skip=1 tokens=3" if all the files follow the same format of URLSpace=Spacehttp://etc.. If you can't depend on that, or if any of the URLs might contain unencoded spaces, you could also change echo(!URL! to echo(!URL:*http=http!
You don't need to use a FOR /F loop, you can also read it with a SET /P
setlocal EnableDelayedExpansion
< stats.txt (
set /p line1=
set /p URL_Line=
)
echo(!URL_Line!
Try this from the command line:
(for /F "tokens=1* delims=:" %i in ('findstr "URL" stats*.txt') do echo %j) > output.txt
the idea ... is to return the 2nd line with URL
If you want to insert this line in a Batch file, just double the percent signs.
Try this from the prompt:
(for /f "tokens=1*delims=]" %a in ('find /v /n "" *.csv^|findstr /l /b "[2]"') do #echo %b)>u:\r1.txt
Where - I used *.csv for testing (substitute your own filemask) and I used u:\r1.txt for the result - substitute as seems fit (but don't output to a file tat fits your selected filemask !)
It works by prefixing each line in each file with a bracketed number [n] (find - /n=and number /v lines that do not match "" - an empty string); then selecting those lines that /l - literally /b at the beginning of the line match "[2]".
The result is all of the second-lines of the files, preceded by the literal "[2]". All we need to do then is tokenise the result, first token up to delimiter "]" will be "[2" assgned to %%a and remainder-of line (token *) will be assigned to %%b
Have you tried
for /F "skip=1 tokens=1 delims=\n" %i in (stats.txt) do echo %i && goto :eof
I haven't tested it as I don't have access to a Windows machine at the moment, but that should exit the for-loop after the first iteration, which is what you want.
I have got a system that generates CSV files containing time based data.
Some files have data from two different dates. I want to break up these files into two files, one containing the data from the first day, the other containing the data from the next day. The original file looks like this:
09.01.2015 00:00:00,0385 - Veerhaven,VP01 in bedrijf hoog toerental,K,Process message.
09.01.2015 00:00:00,0385 - Veerhaven,VP01 in bedrijf laag toerental,G,Process message.
08.01.2015 23:59:55,1475 - Schaatsbaan,PO01 in bedrijf,G,Process message.
08.01.2015 23:59:52,0311 - Abraham van Stolkweg,PO01 in bedrijf,G,Process message.
The first 10 Characters are the date of the event. I want to break up the file in two output files seperating the data from the two days. I have to do this using batch processing because it has to be done every day over a lot of files.
I hope someone can help me on my way. Thanks in advance.
#echo off
setlocal enableextensions disabledelayedexpansion
set "file=c:\somewhere\data.txt"
for %%f in ("%file%") do for /f "usebackq" %%a in ("%%~ff") do (
if not defined %%a (
findstr /b /c:"%%a" "%%~ff" > "%%~dpnf.%%a%%~xf"
set "%%a=1"
)
)
The first for command is used only to retrieve a reference to the file and being able to separate path, filename and extension (that will be used later to generate the output files).
Second for loop reads the input file and for each line retrieves the first token/field in the line using spaces as delimiters (the default behaviour in for /f command). This value is used to filter the input file and declare environment variables:
If the variable is not defined, it is the first time the value is seen, matching records are extracted from the input file to a new output file and the variable is defined.
If the variable is defined, this value has been seen and the corresponding output file generated, the extraction is skipped and the process continues reading the next line.
edited to adapt to comments
#echo off
setlocal enableextensions disabledelayedexpansion
set "files=c:\somewhere\*.txt"
set "outputFolder=c:\where\to\put\files"
for %%f in ("%files%") do (
setlocal
for /f "usebackq" %%a in ("%%~ff") do if not defined %%a (
findstr /b /c:"%%a" "%%~ff" > "%outputFolder%\%%~nf.%%a%%~xf"
set "%%a=1"
)
endlocal
)
The wildcard management in the input needs no changes: for %%f iterates over the indicated set, being it only a file or a set of files.
The output folder is stored in a environment variable. The redirection is changed to use the variable insted of the path of the input file.
As the variables used to determine if the indicated token has been processed needs to be deleted for each file processed, the loop that processes the file contents is wrapped in a pair of setlocal/endlocal that cleans the flag variables after each file has been processed
read HELP FOR to learn how to use the FOR command to loop over the lines of a file and parse its contents. Then, try
for /f "tokens=1,*" %%a in (timedata.txt) do (
echo %%a ... %%b
)
you see that you may use %%a to split the files by date, so you could figure out something like
for /f "tokens=1,*" %%a in (timedata.txt) do (
echo %%b >>timedata.%%a.txt
)
or more generically
set fn=%~dpn1
set fx=%~x1
for /f "tokens=1,*" %%a in (%~1) do (
echo %%b >>%fn%.%%a%fx%
)
First thank you for this great site! I've learned lots of batch scripting from here, but finally got stuck. I was tasked to write a script that will go out and check a specific registry keyword and change the ones that are not correct, on all PCs on the network.
#echo off
SetLocal EnableDelayedExpansion
FOR /F %%a in (C:\batchFiles\computers.txt) DO (
FOR /F "tokens=3" %%b in (reg query "\\%%a\HKLM\SOFTWARE\some\any" /v "Forms Path") do set "var=%%b"
if "%var%" == "\\server\folder\forms\path"
echo %%a was correct
pause
if "%var%" NEQ "\\server\folder\forms\path"
echo %%a was not correct
pause
)
My boss tasked me with this not to long ago and its a little above my head, so i'm trying to learn on the fly. I tried with %errorlevel% and couldn't get it to do what I wanted either.
I had all of my PC names listed in C:\batchFiles\computers.txt. The REG_SZ key from "Forms Path" is a folder located on a network drive. Right now it says that the syntax is incorrect.
If you can understand what i'm trying to do, and have a better suggestion, I'm all ears! Oh and I'd like to output ALL of the results to a text file so I know which PCs were changed, which ones had it correct, and which ones the script couldn't reach.
Thank you so much for your time!
You enabled delayed environment variable expansion, but do not use it. %var% must be written as !var! to make use of delayed expansion as required here.
The syntax used on both if conditions is also not correct.
The registry query output by reg.exe on my computer running Windows XP is:
! REG.EXE VERSION 3.0
HKEY_LOCAL_MACHINE\SOFTWARE\some\any
Forms Path REG_SZ \\server\folder\forms\path
There is first a blank line, next a line with version of reg.exe, one more blank line, a line with registry key and finally on fifth line the data of interest. Therefore I used in the batch code below skip=4 to speed it up. However, the inner loop would produce the right result also without skip=4 and therefore parsing all 5 lines.
Important is the last line. The inner loop separates by spaces. As the name of the registry value contains also a space character, the first two tokens are for Forms and Path. And the third token is REG_SZ.
The rest of the line after the spaces after REG_SZ is of real interest, but could contain also a space character. So I used in batch code below not tokens=4, but instead tokens=3* and ignored %b which holds REG_SZ. Instead %c is assigned to environment variable var resulting in getting really entire string value even if the string contains 1 or more spaces.
And the environment variable var is deleted before a new query on next computer is executed in case of a computer does not contain the registry value at all. The error message written by reg.exe to stderr is redirected to device nul for this case. The value of var would be unchanged from previous computer if not deleted before running the next query.
#echo off
setlocal EnableDelayedExpansion
for /F %%a in (C:\batchFiles\computers.txt) do (
set var=
for /F "skip=4 tokens=3*" %%b in ('%SystemRoot%\System32\reg.exe query "\\%%a\HKLM\SOFTWARE\some\any" /v "Forms Path" 2^>nul') do set "var=%%c"
if "!var!" == "\\server\folder\forms\path" (
echo %%a has correct value.
) else if "!var!" == "" (
echo %%a does not have the value at all.
) else (
echo %%a has wrong value.
)
pause
)
endlocal
I had the below code in one of the make files.
#for /F "skip=2 Delims=" %%i in ('<somefile>') do echo Code-%%i
Could anybody tell what is this batch command would do?
As it is written
#for /F "skip=2 Delims=" %%i in ('<somefile>') do echo Code-%%i
this means: Execute (not read file, execute, there are simple quotes) <somefile>, take its output to standard stream, skip the two first lines, and for each line with content, assign this line contents to %%i, then, also for each line, output to console the text Code- followed by the content of the readed line (that is inside %%i).
The # at the start of the line means not to echo to console the for command.
The delims with no value assigned indicates that lines should not be considered as a concatenation of fields with a delimiter between them, that have to be splitted.
I'm trying to use WMIC to remove some old printers on our older print servers
if I use the below syntax from the command line it works
WMIC PRINTER WHERE "SERVERNAME LIKE '%%OLDSERVERNAME%%'" DELETE
I was asked to see if I could make it replace the printers or update them to spare the helpdesk from getting "how do I add my printer" calls
I tried some variation with SET to set the properties for SERVERNAME, it appear to process, but nothing changes and doing a GET shows the old value still.
so I figured I could do a FOR /F loop, wrap the name of the printer (sharename) in a variable and then use another command to recreate the printers, after the offending printers have been deleted.
here's my script
#ECHO
ECHO Gathering Installed Printer Information...
FOR /F "tokens=1 skip=1" %%A IN ('WMIC PRINTER WHERE "Servername like "%%printaz1%%"" get sharename') DO SET PRINTERNAME=%%A
ECHO %PRINTERNAME%
so you would think you'd get the value all wrapped up in a nice variable to do with as you wish but no, I turned echo on and it appears that the variable gets set back to null as soon as it's captured
my output looks like this...
FOR /F "tokens=1 skip=1" %%A IN ('WMIC PRINTER WHERE "Servername like "%%printaz1%%"" get sharename') DO SET PRINTERNAME=%%A
SET PRINTERNAME=nameofprinter
SET PRINTERNAME=
ECHO
ECHO is on.
would love some thoughts on why it seems to be dumping the string.
Powershell and VBScript aren't out of the option either, I'm familar with both, just this seemed possible, and I really don't want to start over.
WMIC is returning two lines, one with the printername, and one empty.
The solution to this is to test for empty lines.
ECHO Gathering Installed Printer Information...
FOR /F "tokens=1 skip=1" %%A IN ('WMIC PRINTER WHERE "Servername like "%%printaz1%%"" get sharename') DO if not "%%A"=="" SET PRINTERNAME=%%A
ECHO %PRINTERNAME%
what you are looking for is this:
for /f "skip=2 tokens=2 delims=," %%A in ('WMIC PRINTER WHERE "Servername like "%%printaz1%%"" get sharename /format:csv') DO SET PRINTERNAME=%%A
ECHO %PRINTERNAME%
Explanation:
The unicode output of wmic includes a carriage return which is captured by the FOR /F which is what you do not want, as it means the 'enter' becomes part of your variable (%%A) and it also contains an empty line, which again will be outputted to %%A.
My example does the following:
/format:cvs -- will output a comma seperated output so we can capture using the delimiter ,
skip=2 -- will skip the empty line at the beginning and skip the header row of the csv.
tokens=2 -- the csv output contains the name,value as output, but you only want value, which is the second token.
by the way, because we no longer include space as a seperate it should even work with spaces in the value. give it a try.
I got around the UNICODE carriage return (which is really the problem here) like this:
#echo off
FOR /F "usebackq tokens=1,2 delims==" %%A IN (`wmic %* /VALUE ^| findstr "="`) DO (call :OUTPUT %%A "%%B")
exit /b
:OUTPUT
echo %1=%2
Save that as "wmi.bat"
and execute your query like this:
wmi.bat printer where 'name like "HP%"' get name
My output looks like this:
Name="HP1C0BCA (HP Officejet Pro 276dw MFP)"
Name="HP Officejet Pro L7500 Series"
Name="HP Officejet Pro 276dw MFP PCL 6"
You can imagine how you can play with this.
the "%%B" portion puts all of the output of /VALUE (including spaces) into the %2
%%A captures the object id, which becomes %1 which is handy if you use this to log all sorts of information from WMI. You don't have to worry about the Unicode carriage return anymore.
The big advantage of this approach is that you have a clever batch file which overcomes the limitations that wmic gives you when using it inside a script, and you can use it for anything.
you can use the STDOUT of the batch file as the parameter for anything else, even as input to wmic!
The most secure and general variant is to nest another for /F loop, like this:
for /F "skip=1 usebackq delims=" %%A in (`
wmic Printer where 'Servername like "%%printaz1%%"' get ShareName
`) do for /F "delims=" %%B in ("%%A") do set "PRINTERNAME=%%A"
The problem is the Unicode output of the wmic command and the poor conversion to ANSI text by for /F, which leaves some orphaned carriage-return characters, which in turn lets appear some additional lines as non-empty. The second for /F loop gets rid of these conversion artefacts.
I also replaced the tokens=1 option by delims= in order not to truncate the returned item at the first white-space.
Take also a look at the following related answers by user dbenham:
Why is the FOR /f loop in this batch script evaluating a blank line?
For /F with wmic unwanted output
#ECHO
ECHO Gathering Installed Printer Information...
FOR /F "tokens=1 skip=1" %%A IN ('WMIC PRINTER WHERE "Servername like "%%printaz1%%"" get sharename') DO (
SET PRINTERNAME=%%A
goto end
)
:end
ECHO %PRINTERNAME%