Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 months ago.
Improve this question
So I've started getting familiar with Batch for the last year off and on, all self taught, and I've made a few little dinky projects here and there but I'm wanting to find out the best way to go about expanding my code. Using other programs for inspiration I've learned just the tip of the icebergs for "FOR loops" and using functions and small macro processes. I want to know if there is anything I should do differently in terms of architecture and planning so I can one day make amazing Batch programs like T3RROR (little weird but I'm a big fan and your my batch idol).
Below is my most complex batch code I've written and a template for future games, please add any tip or advice.
#echo off
SETLOCAL EnableDelayedExpansion
title crafting micro
color 2
:://///////////////////////////////////////////////////////////////////////
:pre_start
::initializes at the start of the program
set /a display_message=0
set /a inv_fiber=1
set /a fiber_qty=100
set /a inv_rope=0
set /a rope_qty=0
set /a inv_stick=1
set /a stick_qty=100
set /a inv_stone=1
set /a stone_qty=100
set /a inv_sharpened_stone=0
set /a sharpened_stone_qty=0
set /a inv_spear=0
set /a spear_qty=0
set /a inv_handaxe=0
set /a handaxe_qty=0
set /a inv_hammer=0
set /a hammer_qty=0
:://///////////////////////////////////////////////////////////////////////
:start
cls
echo ----------------------------------------------------
echo Crafting Menu
echo ----------------------------------------------------
echo.
echo Raw materials
echo -------------
if %inv_fiber%==1 echo Fiber: x%fiber_qty%
if %inv_stick%==1 echo Stick: x%stick_qty%
if %inv_stone%==1 echo Stone: x%stone_qty%
echo.
echo Refined Materials
echo -----------------
if %inv_rope%==1 echo Rope: x%rope_qty%
if %inv_sharpened_stone%==1 echo Sharpened Stone: x%sharpened_stone_qty%
echo.
echo Tools
echo -----
if %inv_spear%==1 echo Spear: x%spear_qty%
if %inv_handaxe%==1 echo Handaxe: x%handaxe_qty%
if %inv_hammer%==1 echo Hammer: x%hammer_qty%
::sets visibility
echo.
echo ----------------------------------------------------
echo.
echo What do you wanna craft?
echo 1. Rope (x3 Fiber)
echo 2. Sharpened Stone (x2 Stones)
echo 3. Spear (x1 Rope, x1 Stick, x1 Sharpened Stone )
echo 4. Handaxe (x2 Rope, x2 Stick, x1 Sharpened Stone)
echo 5. Hammer (x2 Rope, x2 Stick, x1 Stone)
echo.
choice /c 12345
echo.
echo.
if %errorlevel%==1 (
set item=Rope
::item being crafted
set /a req1=%fiber_qty%
::1st required item
set /a min1=3
:: minimum amount needed to craft
set /a req2=0
set /a min2=0
set /a req3=0
set /a min3=0
set var1=fiber
::passes a string to be use for a vavriable
set var2=
set var3=
set var4=rope
)
if %errorlevel%==2 (
set item='Sharpened Stone'
set /a req1=%stone_qty%
set /a min1=2
set /a req2=0
set /a min2=0
set /a req3=0
set /a min3=0
set var1=stone
set var2=
set var3=
set var4=sharpened_stone
)
if %errorlevel%==3 (
set item=Spear
set /a req1=%rope_qty%
set /a min1=1
set /a req2=%stick_qty%
set /a min2=1
set /a req3=%sharpened_stone_qty%
set /a min3=1
set var1=rope
set var2=stick
set var3=sharpened_stone
set var4=spear
)
if %errorlevel%==4 (
set item=Handaxe
set /a req1=%rope_qty%
set /a min1=2
set /a req2=%stick_qty%
set /a min2=2
set /a req3=%sharpened_stone_qty%
set /a min3=1
set var1=rope
set var2=stick
set var3=sharpened_stone
set var4=handaxe
)
if %errorlevel%==5 (
set item=Hammer
set /a req1=%rope_qty%
set /a min1=2
set /a req2=%stick_qty%
set /a min2=2
set /a req3=%stone_qty%
set /a min3=1
set var1=rope
set var2=stick
set var3=stone
set var4=hammer
)
::////////////////////////////////////////////////////////////////////////////////////
:craftable_check
if %req1% GEQ %min1% (
if %req2% GEQ %min2% (
if %req3% GEQ %min3% (
::checks minimum requried material inventory for crafting
set /a %var1%_qty=!%var1%_qty!-%min1%
set /a %var2%_qty=!%var2%_qty!-%min2%
set /a %var3%_qty=!%var3%_qty!-%min3%
::uses all the required inventory for crafting
set /a %var4%_qty=!%var4%_qty!+1
::creates the crafted item
set /a inv_%var4%=1
::makes the new item is visible
if %req1% LEQ 0 (
set inv_%var1%=0
)
if %req2% LEQ 0 (
set inv_%var2%=0
)
if %req3% LEQ 0 (
set inv_%var3%=0
)
::makes the items ivisible if there is no inventory
set /a display_message=1
set message='You have successfully created a %item%!'
goto message
)
)
)
set /a display_message=1
set message=You dont have enough material for this
goto message
:://///////////////////////////////////////////////////////////////////////////////////
:message
cls
if %display_message%==1 (
echo %message%
pause
set /a display_message=0
goto start
)
::General Settings ------------------------------------------------------------------------------
#echo off
title Game Template
color 02
cd "%userprofile%\desktop"
::Game Settings ---------------------------------------------------------------------------------
:game_settings
cls
goto welcome
::Welcome Screen --------------------------------------------------------------------------------
:welcome
cls
echo Welcome to this Game!
echo/
echo Please select one of the options below
echo 1. New Game
echo 2. Load Game
echo 3. Credits
choice /c 123
if %errorlevel%==1 goto back_story
if %errorlevel%==2 goto load_game
if %errorlevel%==3 goto credits
goto welcome
::Save Game -------------------------------------------------------------------------------------
:save_game
cls
(
echo %location%
echo %player_name%
)>"GameSave.txt"
echo Game saved...
pause
goto :eof
::Load Game -------------------------------------------------------------------------------------
:load_game
cls
(
set /p location=
set /p player_name=
)<"GameSave.txt"
pause
goto %location%
::Back Story ------------------------------------------------------------------------------------
:back_story
cls
echo Backstory...
pause >nul
goto character_creation
::Character Creation ----------------------------------------------------------------------------
:character_creation
cls
echo What is your name?
set /p player_name=
if /i "%player_name%"=="" (
echo Player name can not be blank.
pause >nul
goto character_creation
)
goto start
::Game Start ------------------------------------------------------------------------------------
:start
cls
set location=start
echo %player_name%
echo %location%
echo/
echo 1. Pause Menu
choice /c 1
if %errorlevel%==1 goto pause_menu
goto start
::Pause Menu ------------------------------------------------------------------------------------
:pause_menu
cls
echo 1. Save Game
echo 2. Return
echo 3. Quit
choice /c 123
if %errorlevel%==1 call :save_game
if %errorlevel%==2 goto %location%
if %errorlevel%==3 goto exit
goto pause_menu
::Credits ---------------------------------------------------------------------------------------
:credits
cls
echo credits...
pause >nul
goto welcome
if %req3% GEQ %min3% (
::checks minimum requried material inventory for crafting
set /a %var1%_qty=!%var1%_qty!-%min1%
won't work properly - :: comments are actually broken labels (labels that cannot be reached with a goto) and break code blocks (parenthesised series of statements)
Reserve a prefix character for any variable that you may want to save to a file for reloading later. EG #
set #>filename
saves all of the variables that start # to the file in the form #whatever=something.
Reload with
for /f "delims=" %%e in (filename) do set "%%e"
Equally, you can set up scenario files by choosing another prefix character.
Clearing the current values of variables-beginning-# is
for/f "delims==" %%e in ('set # 2^>nul') do set "%%e="
which you do just before loading from file.
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, Space or " - build pathnames from the elements - counterintuitively, it is likely to make the process easier. If the syntax set var="value" is used, then the quotes become part of the value assigned.
set up a menu subroutine using choice
:menu
set "choices="
set /a choicecount=0
cls
:menu_next
set /a choicecount+=1
if defined choicetext[%choicecount%] echo %choicecount%. choicetext[%choicecount%]&set "choices=%choices%%choicecount%"
if %choicecount% lss 9 goto menunext
echo Q. Quit
choice /c q%choices% /N /M "%~1"
goto :eof
This way, you can set up the available choices in choicetext[?] (and clear them with
for/f "delims==" %%e in ('set choicetext[ 2^>nul') do set "%%e="
) then display the menu by executing
call :menu "Some message"
When `:menu" returns, you can then
GOTO Someplace%errorlevel%
which will goto someplace2 etc. Note that because q is always the first character of the choices, then q will always return errorlevel 1, hence the label :whatever1 will mean q was pressed. That way, you don't need to alter the quit code should you extend the menu.
(X for eXit works, too.)
Related
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%
I am making a simple text game. Everything works fine until it gets to the if statement. I do not know what i am doing wrong.
#echo off
title Text Adventures
echo "What is your name?"
set /p name=
echo "That was a bad crash. Are you hurt, %name%?"
echo.
echo Y/N
set /p answer=
if %answer% equ y goto hurt
:hurt
echo.
echo "I see. That's a gnarly gash you've got there!"
Here I have re-written this to be in a form that uses Functions and uses the suggestions I provided to the original post as comments.
You know I assumed incorrectly that you had a purpose to using the labels, but given the way the question has give I think you would do well to not bother with them.
Here is a no-labels version:
#(SETLOCAL
echo off
title Text Adventures
)
CALL :Main
( ENDLOCAL
ECHO. The Script Ended!
PAUSE
EXIT /B
)
:Main
set /p "name=What is Your Name?"
ECHO.
CHOICE /M "That was a bad crash. Are you hurt, %name%?"
If %ERRORLEVEL% equ 1 (
echo.
echo "I see. That's a gnarly gash you've got there!"
) ELSE (
echo.
echo "Oh Good!"
)
PAUSE
GOTO :EOF
Here is the labels version. (I removed the comments explaining how it works and why the code is there so it isn't so cluttered for you.)
#(SETLOCAL
echo off
title Text Adventures
)
CALL :Main
( ENDLOCAL
ECHO. The Script Ended!
PAUSE
EXIT /B
)
:Main
set /p "name=What is Your Name?"
ECHO.
CHOICE /M "That was a bad crash. Are you hurt, %name%?"
If %ERRORLEVEL% equ 1 (
CALL :Hurt
) ELSE (
CALL :NotHurt
)
PAUSE
GOTO :EOF
:Hurt
echo.
echo "I see. That's a gnarly gash you've got there!"
GOTO :EOF
:NotHurt
echo.
echo "Oh Good!"
GOTO :EOF
For this sort of thing I prefer by far using choice, and frequently use my menu macro that returns the selected option as a string in the variable option
below is a shell script that demonstrates the usage of a few macro's I frequently use.
#ECHO Off & Goto :main
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::: * Functions * :::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:+------------------------------------------------------------------------------------------------+:
:ColorString
(For %%C in (%~1)Do Set "rv=!rV!%/E%!#A!m%%~C%/E%0m"& Set /A "#A+=1"& IF "!#A!"=="37" (Set #A=31))
Exit /B 0
:+------------------------------------------------------------------------------------------------+:
:ColorMenu
rem Set /A "RR=!random! %%195 + 60","GG=!random! %%195 + 60","BB=!random! %%195 + 60"
rem Set "%~1=%/E%38;2;!RR!;!GG!;!BB!m!%~1!%/E%0m"
Set "rV="& IF "!#A!"=="" (Set #A=32)
(For %%C in ("!%~1!")Do Set "rv=!rV!%/E%!#A!m%%~C%/E%0m"& Set /A "#A+=1"& IF "!#A!"=="37" (Set #A=31)) & Set "%~1=!rV!"
Exit /B 0
:+------------------------------------------------------------------------------------------------+:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:main * Environment Definition * :::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Rem /* ensure correct environment for macro definitions */
Endlocal & Setlocal DISABLEdelayedexpansion
:+------------------------------------------------------------------------------------------------+:
For /F %%a in ('echo prompt $E ^| cmd')do (Set "/E=%%a[")
:+------------------------------------------------------------------------------------------------+:
Set "ChoList=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Set "ColLETTERS=Set "Rv="& Set "#A=31" & (For %%A in ("$Str")Do Set "Word=%%~A" & For %%B In (0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z . + - _ [ ] / \ ":")Do Set "Word=!Word:%%~B=%%~B !") & (Call :ColorString "!Word!" & Echo/!rV!)"
Set "ColWORD/S=Set "#A=35"& Set "MSG=$Str" & For %%G in ("!MSG!")Do (For %%v in (%%~G)Do (Set "Rv="&Call :Colorstring "%%~v"& For %%x in (!Rv!)Do (Set "MSG=!MSG:%%~v=%/E%0m%%~x!")))&Echo/!MSG!"
Set "List/D/V=Set "#$L=0"&Set "$$L="&For %%n in (1 2)Do if %%n==2 (For %%G in (!$L!)Do (Set "%%~G" > Nul &Set /A "#$L+=1"))Else Set $L="
Set "Menu=For %%n in (1 2)Do if %%n==2 (!DIV!&Set "CH#=0"&(Set "CHCS="&For %%G in (!Options!)Do For %%i in (!CH#!)Do (Set "CHCS=!CHCS!!ChoList:~%%i,1!"&Set "Opt[!ChoList:~%%i,1!]=%%~G"& Set "Opt=%%~G" &Call :ColorMenu Opt &<Nul Set /P "=[!ChoList:~%%i,1!] !Opt!"&Set /A "CH#+=1"&Echo/))&!DIV!& For /F "Delims=" %%o in ('Choice /N /C:!CHCS!')Do (Set "OPTION=!Opt[%%o]!"))Else Set Options="
Set "ColLINE=For %%n in (1 2)Do if %%n==2 (Echo/!$C!!$Str!%/E%0m)Else Set $Str="
:+------------------------------------------------------------------------------------------------+:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::: * Script Body * :::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Setlocal EnableDelayedExpansion
for /F "usebackq tokens=2* delims=: " %%W in (`mode con ^| findstr Columns`) do Set "Console_Width=%%%W"
Set "DIV="&For /L %%i in (2 1 %Console_Width%)Do Set "DIV=!DIV!-"
Set "DIV=Echo/%/E%33m!DIV!%/E%0m"
%List/D/V:$L=Colors%"Red=%/E%31m" "Yellow=%/E%33m" "Green=%/E%32m" "Blue=%/E%34m" "Purple=%/E%35m" "Cyan=%/E%36m" "White=%/E%37m" "Gray=%/E%90m" "Pink=%/E%91m" "Beige=%/E%93m" "Aqua=%/E%94m" "Magenta=%/E%95m" "Teal=%/E%96m" "Off=%/E%0m" "Black=%/E%30m"
%List/D/V:$L=Actions%"Buy a house=Did you win the lotto" "Go for a drive=Start your engines" "Walk on the wild side=Get mauled by a lion" "Run a mile=Fit yet" "flee the realm=fall down a hole" "battle trolls=A losing battle" "puzzle a conundrum=Enigma's Stigmatised" "solve 42=life is meaningless"
%ColLETTERS:$Str=Welcome.%
%ColWORD/S:$Str=Hello there my friend^! How are you today.%
%ColLINE:$C=Teal%What a world we live in^^!
%Menu%"Buy a house" "Go for a drive" "Walk on the wild side" "Run a mile" "flee the realm" "battle trolls" "puzzle a conundrum" "solve 42"
For %%G in ("!Option!")Do Echo/!%%~G!
The menu macro builds a list of choices for each argument it's supplied, using substring modification to iterate over a 36 character choice list to build the final choice options - meaning support for 36 menu options.
Update
An additional example has been added to show a way to use the selection to display output or take actions without requiring conditional testing. Using the List/D/V macro to define values to options, a selected OPTION can be expanded directly. In the example, it's just to display output, however values can be assigned containing simple commands like Set, Goto or call to perform specific actions for the chosen menu option.
I forgot to add pause
#echo off
title Text Adventures
echo "What is your name?"
set /p name=
echo "That was a bad crash. Are you hurt, %name%?"
echo.
echo Y/N
set /p answer=
if %answer% equ y goto hurt
:hurt
echo.
echo "I see. That's a gnarly gash you've got there!"
pause
So. Currently I am working on a game where you create your own nation in Batch. There are bills passed by congress that you can either sign or veto. Once you do so, the bill disappears. After some time a new bill will pop up. The thing is there is a bill limit of 5, meaning that there can only be 5 bills waiting your approval at a time.
Here is the code so far.
:leg
title Borderlines BETA - Legislation
cls
echo =====
echo 1-Legislation (%notify_leg%) ===== 2-Disputes (%notify_disputes%) ===== 3-Mailbox (%notify_mailbox%)
echo =====
echo Legislation
if %notify_leg% GTR 0 (
echo You have new bills from Congress awating your approval!
) else (
echo Its been a slow day in %nation%...
)
echo ---
echo Select a bill...
echo %leg_01%
echo %leg_02%
echo %leg_03%
echo %leg_04%
echo %leg_05%
echo.
echo ---
echo A - Home
echo.
echo ---
set /p leg_choice=""
if %leg_choice%==A goto home
if %leg_choice%==1 goto leg
if %leg_choice%==2 goto disputes
if %leg_choice%==3 goto mail
if %leg_choice% GTR 3 goto bill_%leg_choice%
goto home
Basically %leg_01%, %leg_02%, and so on are the slots. When you create your nation, the bills are assigned. When you pass or veto a bill it removes the notification.
However lets say over time another bill gets added to the list. How can I have it so that once you sign or veto the bill it will remove it from the list, tell the program that a slot is available, and move up the remaining items all the way to the top. This would be essential because there could be, eventually, multiple bills in the game that will show up on that list.
Hope that makes sense ~
Does this suit your needs?
:newleg
if "%~1"=="" exit /b
for /l %%# in (4 -1 1) do (
set /a n=%%#+1
call set leg_0%%n%%=%%leg_0%%#%%
)
set leg_01=%~1
Call with call :newleg This_is_a_new_leg. All values will be shifted downwards, i.e. leg_05 is lost, the value of leg_04 is assigned to leg_05. leg_01 will be redefined as the specified parameter.
If you'd like to avoid quoting values with spaces while calling the function, you can use %* instead.
You can also keep the values of leg_06 and so on by increasing the limit in the for-loop (n-1, in this case "4").
You can delete an item from the list and then shift upwards with this function:
:delleg
if "%~1"=="" exit /b
set leg_0%1=
for /l %%# in (1 1 5) do if not defined leg_0%%# call :dl %%#
exit /b
:dl
set /a n=%1+1
for /l %%# in (%n% 1 6) do (
set /a n=%%#-1
call set leg_0%%n%%=%%leg_0%%#%%
)
exit /b
Call with call :delleg 2 to delete leg_02.
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}");
}
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