Extract a number from string in batch script - regex

I've been struggling to write a script that will find the drive index number from other properties of the drive. The script is as follows:
#echo off
REM batch file to load Veracrypt
Set "driveIndex="
for /f "skip=1 tokens=1 delims= " %%a in ('wmic diskdrive where "model ='WD Elements 1078 USB Device'" get index') do SET driveIndex=%%a & goto reportLetter
:reportLetter
if not defined driveIndex (
echo Error Occured!
pause
exit
) else (
echo \Device\Harddisk%driveIndex:~0%\Partition3
pause
exit
)
However, the output of the script is \Device\Harddisk1 \Partition3. I tried for a long long time but could get the script to give the following output: \Device\Harddisk1\Partition3.
Could anyone tell me how to correct the code to get the required output?

Try this
DO SET "driveIndex=%%a"
Your line
... do set driveIndex=%%a & goto ...
is interpreted as set driveIndex=%%a<space>& goto ..., this is, where the additional space in \Device\Harddisk1 \Partition3 comes from.
Of course you could write:
... do set driveIndex=%%a& goto ...
but the better syntax is:
... do set "driveIndex=%%a" & goto ...
which eliminates any unintended spaces.
Note1: set is very picky with spaces. set var = hello creates a variable named var<space> with the value <space>hello<space>.
Note2:
set var="value" sets the value to "value"
set "var=value"sets the value to value. This syntax gives you full (and visible) control to the variable name and it's value.

#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET DRIVE_INDEX=
FOR /F "usebackq skip=1" %%i IN (`wmic diskdrive where "model = 'HGST HTS725050A7E630 ATA Device'" get index`) DO (
IF "!DRIVE_INDEX!" EQU "" (SET DRIVE_INDEX=%%i)
)
ECHO DRIVE_INDEX is set to %DRIVE_INDEX%

I believe that the problem is that WMIC output is Unicode.
I'd try
for /f "skip=1 tokens=1 delims= " %%a in ('wmic diskdrive where "model ='WD Elements 1078 USB Device'" get index^|more') do SET driveIndex=%%a & goto reportLetter
where the ^|more converts to ANSI. The caret escapes the pipe to tell cmd that the pipe is part of the command to be executed.

Related

Batch Script - How to check is variable is empty

hope someone can help resovle the issue I'm having.
The script asks user to provide old serial number and PC location than searches a CSV file for the SN and copies the row to a text file. Once the row is in the text file is pulls the SN and Hostname and set them into 2 variables ( the actual CSV has 10 columns). The 2 variables are later passed to a log file.
Problem I have is that if the user provided SN is not in the CSV it does not get recorded, which is needed.
Been reading over google searches and tried several different but still have same issue with losing SNs not found.
Of all the IF statements this one has worked in the pass... well I think it did.
If [%aHostname%]==[] (set oSerial=%icomputerSN%)
So the question I have is how do I get the script to hold the user provided variable if the variable is not in the CSV file?
sitepacket.svc
Serial # Host Name
2UA6492B71 0DC4A3E551DC2
MXL8361932 010E7C6B23577
MXL8321WBS 010E7C6B269C1
MXL8382H8G 0F43909049EF6
MXL8321VQ4 010E7C6B27034
MXL8331GNZ 010E7C6B7407D
MXL8331GMY 010E7C6B73961
MXL8331GPP 010E7C6B74021
MXL8331G26 010E7C6B739EF
MXL8321WFK 010E7C6B26A6B
MXL8331G1Q 010E7C6B73CF6
MXL8331GPN 010E7C6B7400C
MXL8331G3M 010E7C6B73CF7
MXL8321WBL 010E7C6B269B9
MXL8331G34 010E7C6B73D26
Script
#echo off
setlocal enabledelayedexpansion
set "au=TestRUN"
set "InstLog=%~dp0\AU%au%.csv"
net session >nul 2>&1
REM :: Get New workstations IP and set in var
set "ip="
for /f "tokens=2 delims=:" %%a in ('ipconfig ^| findstr /c:"IPv4 Address"') do set ip=%%a
:LogFileCheck
if exist %InstLog% goto :GetSN else
echo Location,Old_SN,Old_Host,Replaced_By,New_PC_IP,New_Serial,New_Host > %InstLog%
goto :GetSN
REM # Input field for tech to provide Old SN and old workstation location.
:GetSN
Echo What is the old computer serial number?
set /p icomputerSN=
Echo What is the location and location number of workstation?
set /p PcLoc=
findstr /i "%icomputerSN%" "%~dp0\Sitepacket.csv" > "%~dp0\oldsystems.txt"
FOR /F "usebackq tokens=1-9 delims=," %%a in ("%~dp0\oldsystems.txt") DO (
set aSerial=%%a
set aHostname=%%b
If [%aHostname%]==[] (set oSerial=%icomputerSN%)
)
for /f "skip=1 tokens=*" %%C in ('wmic bios get serialnumber') do if not defined serial set serial=%%C
set nserial=%serial: =%
for /f "skip=1 delims=" %%A in (
'wmic computersystem get name'
) do for /f "delims=" %%B in ("%%A") do set "nHost=%%A"
set "output=%PcLoc%,%oserial%,%oHostname%,Replace with,%nserial%,%nHost%,%IP%"
echo %output% >> %InstLog%

Remotely get serial number and model from list in batch script

I am trying to create a simple script that will use computer name from pclist.txt and get serial number and model and export to csv file. I just can not seem to figure out why it does not print the serial number and model in the csv file. Here is my simple script.
#echo off
for /f %%i in (pclist.txt) do (
for /f "tokens=2 delims==" %%M in ('wmic /node:%%i csproduct get name /value') do set "Model=%%M"
for /f "tokens=2 delims==" %%I in ('wmic /node:%%i bios get serialnumber /value') do set "SN=%%I"
echo %%i, %SN%, %Model% >> list.csv
)
pause
I may have misunderstood what your intention was, but I would have expected the following to be the general way of achieving your intended task.
#%__AppDir__%wbem\WMIC.exe /Node:#pclist.txt /FailFast:1000 /Output:list.csv CSProduct Get Name /Format:CSV
#%__AppDir__%wbem\WMIC.exe /Node:#pclist.txt /FailFast:1000 /Append:list.csv BIOS Get SerialNumber /Format:CSV
#Pause
If you wanted to try to just output each item along side the node name separated by commas, then perhaps this will suffice:
#Echo Off
SetLocal EnableExtensions
(
For /F UseBackQDelims^=^ EOL^= %%G In (
"pclist.txt"
) Do (
For /F Tokens^=6Delims^=^" %%H In (
'%__AppDir__%wbem\WMIC.exe /Node:%%G /FailFast:1000 CSProduct Get Name /Format:MOF'
) Do (
For /F Tokens^=6Delims^=^" %%I In (
'%__AppDir__%wbem\WMIC.exe /Node:%%G /FailFast:1000 BIOS Get SerialNumber /Format:MOF'
) Do Echo %%G,%%H,%%I
)
)
)>"list.csv"
Pause
Your code would run fine - when wmic wouldn't have that ugly line ending of CRCRLF. The first CR becomes part of your variable. CR means "set the cursor back to the beginning of the line", so everything echoed after (in the same line) overwrites the previous content.
You can solve that with another for loop to strip the remaining CR. And you don't need the variables SN and Model; when you nest the for loops, you can use the for variables directly instead:
#echo off
del list.csv 2>nul
for /f %%i in (pclist.txt) do (
for /f "tokens=2 delims==" %%M in ('wmic /node:"%%i" csproduct get name /value') do for %%m in (%%M) do (
for /f "tokens=2 delims==" %%S in ('wmic /node:"%%i" bios get serialnumber /value') do for %%s in (%%S) do (
>>list.csv echo %%i, %%s, %%m
)
)
)
type list.csv
pause
(Compo's code works, because the desired string is cut from the middle of a line, so the line ending doesn't matter)

wshShell and WMIC working together in batch?

I was trying to create a scrollable list for a batch file using the method found here: Scrollable Lists in .bat Files
Credit to https://stackoverflow.com/users/778560/aacini
One I tried adding back in my code for my regular batch a few lines at a time, I noticed that the solution doesn't work when I use WMIC. What is the reason for this and is there an easy solution? You can run the below code and then un-comment the WMIC line and see it will not work anymore.
Edit: I am using Windows 7
Thanks!
#if (#CodeSection == #Batch) #then
#echo off
setlocal EnableDelayedExpansion
color F0
::FOR /F "tokens=2 delims==" %%A IN ('WMIC csproduct GET Name /VALUE ^| FIND /I "Name="') DO SET machine=%%A
Echo Reset or Continue
Set MenuOptions=RESET Continue
call :ShowMenu
pause
exit
:ShowMenu
set numOpts=0
for %%a in (%MenuOptions%) do (
set /A numOpts+=1
set "option[!numOpts!]=%%a"
)
rem Clear previous doskey history
doskey /REINSTALL
rem Fill doskey history with menu options
cscript //nologo /E:JScript "%~F0" EnterOpts
for /L %%i in (1,1,%numOpts%) do set /P "var="
rem Send a F7 key to open the selection menu
cscript //nologo /E:JScript "%~F0"
set /P "MenuSelected=Option Selected: "
echo/
#end
var wshShell = WScript.CreateObject("WScript.Shell"),
envVar = wshShell.Environment("Process"),
numOpts = parseInt(envVar("numOpts"));
if ( WScript.Arguments.Length ) {
// Enter menu options
for ( var i=1; i <= numOpts; i++ ) {
wshShell.SendKeys(envVar("option["+i+"]")+"{ENTER}");
}
} else {
// Enter a F7 to open the menu
wshShell.SendKeys("{F7}");
}
SendKeys Method sends one or more keystrokes to the active window. Unfortunately, Sendkeys() sometimes fails usually due to focus or timing, both troublesome and difficult to solve problems. I tried pause and timeout, tried WshShell.AppActivate in various combinations, all without success…
Finally, I found culprit in clearing previous doskey history via doskey /REINSTALL command: it installs a new copy of doskey. I can recommend more intuitive (and less invasive) method to clear the doskey command history buffer:
doskey /ListSize=0
doskey /ListSize=50
Bonus: FOR loops explanation.
outer %%A loop to retrieve the name property;
inner %%a loop to remove the ending carriage return in the value returned: wmic behaviour: each output line ends with 0x0D0D0A (<CR><CR><LF>) instead of common 0x0D0A (<CR><LF>).
Credit: Dave Benham's WMIC and FOR /F: A fix for the trailing <CR> problem
Working script (Windows 8.1 64 bit):
#if (#CodeSection == #Batch) #then
#echo OFF
setlocal EnableDelayedExpansion
REM color F0
FOR /F "tokens=2 delims==" %%A IN ('
WMIC csproduct GET Name /VALUE ^| FIND /I "Name="
') DO FOR %%a in ("%%~A") DO SET "_machine=%%~a"
Echo Reset or Continue %_machine%
Set "MenuOptions=%_machine% RESET Continue" expanded merely for debugging purposes
call :ShowMenu
REM pause
exit /B
:ShowMenu
set numOpts=0
for %%a in (%MenuOptions%) do (
set /A numOpts+=1
set "option[!numOpts!]=%%a"
)
rem Clear previous doskey history
REM this causes problems: doskey /REINSTALL
doskey /ListSize=0
doskey /ListSize=50
rem Fill doskey history with menu options
cscript //nologo /E:JScript "%~F0" EnterOpts
for /L %%i in (1,1,%numOpts%) do set /P "var="
rem Send a F7 key to open the selection menu
cscript //nologo /E:JScript "%~F0"
set /P "MenuSelected=Option Selected: "
echo/
goto :eof
#end
var wshShell = WScript.CreateObject("WScript.Shell"),
envVar = wshShell.Environment("Process"),
numOpts = parseInt(envVar("numOpts"));
if ( WScript.Arguments.Length ) {
// Enter menu options
for ( var i=1; i <= numOpts; i++ ) {
wshShell.SendKeys(envVar("option["+i+"]")+"{ENTER}");
}
} else {
// Enter a F7 to open the menu
wshShell.SendKeys("{F7}");
}

how to create list element only if it not exist already in batch file?

i want to create batch script which will read *.txt file like this one
05.2016 192
04.2016 431
05.2016 29
12.2015 110
12.2015 42
12.2015 7584
12.2015 1118
04.2016 0
12.2015 356
05.2016 140
05.2016 248
04.2016 2012
and create list element only if the first token is new and add the second token to its value,
if it is used already i want to add the second token in addition to it previous value
for example list[04.2016] would have value 2443 (431+0+2012)
i tried this code
#echo off
setlocal enabledelayedexpansion
for /F "tokens=1,2" %%i in (text.txt) do (
if pole[%%i]==nul set pole[%%i]=%%j
::else ( set pole[%%i]=pole[%%i]+%%j )
echo !pole[%%i]!
)
echo !pole[04.2016]!
echo !pole[05.2016]!
( the else line is only for preview what i want to do next :-D )
i expect that i have some mistake in the line with IF because when i try simplier code with only
for /F "tokens=1,2" %%i in (spracuj2.txt) do (
set pole[%%i]=%%j
echo !pole[%%i]!
)
it works fine but i need the if statements though :-P
You underestimate the power of set. Your if statement does not work (and you don't need it). You have to use set /a to do calculations:
#echo off
setlocal enabledelayedexpansion
for /F "tokens=1,2" %%i in (test.txt) do set /a pole[%%i]+=%%j
echo !pole[04.2016]!
echo !pole[05.2016]!
echo ----
set pole[
(you don't need the if, because set /a var=%emptyVariable% sets var to 0 (very convenient))

monitoring a log file for a specific pattern

I am trying to write a batch file to monitor a log file for the word 'rdy' on a line and alert if the value against rdy is less than 200
extract from my log file as below:
[Sun Jun 23 11:00:00 2013] [notice] mpmstats: rdy 249 bsy 1 rd 0 wr 1 ka 0 log 0 dns 0 cls 0
[Sun Jun 23 11:00:02 2013] [error] [client 10.25.134.1] File does not exist: E:/htdocs/default/KeepAlive.html
I have written a basic script ( Still on my L's ) which monitors the error.log file in a particular directory. The issue is there are error logs from multiple days and I want to monitor the current error log.
#echo off
set log=E:\scripts\busycheckalert.log
set Time=%time:~0,5%
set Today=%date:~4,2%
set Month=%date:~7,2%
set Year=%date:~12,2%
set file=E:\logs\ihs\Default\error.log.%Month%.%Today%.%Year%
echo Polling %file% at %Time% >> %log%
for /f "usebackq delims=;" %%a in (`dir /b E:\logs\ihs\Default\error.log.%Month%.%Today%.%Year%`) do (
echo Checking now >> %log%
for /f "tokens=8,9 delims= " %%a in (E:\logs\ihs\Default\error.log.%Month%.%Today%.%Year%) do (
echo Doing Checks >> %log%
if %%j LEQ 200 echo %Today%-%Month%-%Year% at %Time% Error - Ready threshold exceeded >> %log% in %%a ))
I manage to get till the first checkpoint " Checking now". However, it seems it doe not enter that 2nd loop.
This is the extract from the resultant log file:
Polling E:\logs\ihs\Default\error.log.06.23.13 at 22:48
Checking now
Polling E:\logs\ihs\Default\error.log.06.23.13 at 22:49
Checking now
Polling E:\logs\ihs\Default\error.log.06.23.13 at 22:50
Checking now
Could you please advise where I am going wrong? Any help would be great.
Thanks
for /f "tokens=8,9 delims= " %%a in (E:\logs\ihs\Default error.log.%Month%.%Today%.%Year%) do (
Hmmm- now I wonder what would happen if you were to change the %%a to, say, %%i ?
You appear not to be checking that %%i==rdy either. If you don't do that, you may land up with some rather odd results.
The issue is there are error logs from multiple days and I want to monitor the current error log.
for /f "delims=" %%a in (' dir /b /a-d /od *.log ') do set "latest_file=%%a"
I gather now that it's not your aim.
You seem to be using the same log file name as the file you are processing.
You have delims=; where there are no ; in your log snippet.
You are reusing %%a in both loops.
set file=E:\logs\ihs\Default\error.log.%Month%.%Today%.%Year%
echo Polling %file% at %Time% >> %log%
for /f "usebackq delims=;" %%a in (`dir /b E:\logs\ihs\Default\error.log.%Month%.%Today%.%Year%`) do (
echo Checking now >> %log%
for /f "tokens=8,9 delims= " %%a in (E:\logs\ihs\Default\error.log.%Month%.%Today%.%Year%) do (
Suggestion with code for GNUWin32 grep:
#echo off & setlocal
set "cTime=%time:~0,5%"
set "Today=%date:~3,2%"
set "Month=%date:~6,2%"
set "Year=%date:~11,2%"
set "file=E:\logs\ihs\Default\error.log.%Month%.%Today%.%Year%"
SET "log=resultant.log"
echo Polling %file% at %Time% >> "%log%"
for /f "delims=" %%a in ('dir /b /a-d "%file%') do (
echo Checking now >> "%log%"
for /f "tokens=3" %%b IN ('grep -o "mpmstats: rdy [0-9]\+" "%file%"') do SET "rdy=%%b"
echo Doing Checks >> "%log%"
SETLOCAL ENABLEDELAYEDEXPANSION
if !rdy! LEQ 200 echo %Today%-%Month%-%Year% at %cTime% Error - Ready threshold exceeded IN %%a >> "%log%"
endlocal
)
You are using a specific file in your FOR loops, so there is no need for two of them. The first one merely confirms that the file exists. That is easier and more efficiently done with:
if exist "E:\logs\ihs\Default\error.log.%Month%.%Today%.%Year%" ( ... )
Others have said you have a problem using %%a for both loops. Actually, there is nothing wrong with reusing a character within nested loops. But then your inner loop cannot access the outer loop value. Given that your inner loop DO references %%j, I suspect you intended for your inner loop to use %%i instead of %%a.
Your logic is wrong in that your loop is processing all lines, when it should only process lines that contain " rdy ". FIND or FINDSTR can be used to efficiently filter out unwanted lines.
You should never assign your own value to a variable named TIME (note that variable names are case insensitive). Doing so prevents you from later accessing the dynamic time value.
I haven't figured out what would prevent entry to your inner loop when the outer loop works. But I would restructure your entire code.
Instead of deriving the name of the log file from the current date, I would list all log files in date/time order and use FOR /F to capture the last one found.
Then I would use another FOR /F to parse the output of a FINDSTR search for " rdy "
#echo off
setlocal
set "log=E:\scripts\busycheckalert.log"
set "checkTime=%time:~0,5%"
pushd "E:\logs\ihs\Default"
set "currentLog="
for /f "delims=" %%F in ('dir /b /a-d /od "error.log.*"') do set "currentLog=%%F"
if defined currentLog (
>>"%log%" echo Polling %currentLog% at %checkTime%
for /f "tokens=9" %%A in ('findstr /c:" rdy " "%currentLog%"') do (
if %%A leq 200 >>"%log%" echo Error at %time%: Ready threshold exceeded in %currentLog%
)
) else >>"%log%" echo No log found at %checkTime%"
popd