Advanced Windows Batch File Scripting
Advanced Windows Batch File Scripting
First off, this is a single windows .bat file that I have written to do advanced batch scripting the
easy way, meaning it's mostly a series of functions you can call from the script or within other
functions for extremely modular code. Before you get all bent out of shape by my choice of
words ("easy, modular"), when I say this is advanced I mean for Windows .bat files, one of the
worlds worst scripting languages, but it works on all windows versions so it's ideal for things like
autorun, autoplay, custom startups, usb drives, etc.. If you are looking for information on how to
use and program windows .bat files to do anything cool, this is the right place! I tried my best to
mimic linux shell-scripting, so it's likely different than other batch files you have seen.
Contents [hide]
My favorite tool (and I've tried sooo many) for editing most Windows files and especially .bat
files is the free and open-source Notepad++. Set that up and you will have a color-syntax-
highlighted editor for Batch Scripting that works very very well.
The next thing to do is download the source code, which includes comments and formatting I
had to remove for this online article. Then rename from .txt to .bat and open in your IDE/text-
editor of choice.
One of the first hacks for batch files is line 1, the PUSHD command cd's the scripts working
environment to the directory of the script itself.
:PINGCHECK
ECHO CHECKING %~1
ping.exe -n 1 %~1 -w 5000 | FIND /C "Reply from %~1" | FIND "1" >nul 2>&1
IF ERRORLEVEL 1 CALL :PINGCHECK "%~1" ELSE EXIT /B
Almost forgot, check out the ways to keep a plink.exe (putty.exe for cmd.exe) SOCKS5 tunnel
hidden and safe and continuously connected to a remote server in minimal lines od code. This
was a fun one to work on! Enjoy (and remember this is just the warm-up example to glance at).
@ECHO OFF && PUSHD "%~dp0" && SETLOCAL && SETLOCAL ENABLEDELAYEDEXPANSION &&
SETLOCAL ENABLEEXTENSIONS
SET _CRYPTDRIVE=%~d0
SET PATH=%_CRYPTDRIVE%PP;%_CRYPTDRIVE%PPbin;%PATH%
SET _PUTTYBIN=%_CRYPTDRIVE%PPPputty.exe
SET _PSERVER=solar.power.com
SET _PPORT=22
SET _PSESSION=newclean-tunnel
SET _ADMINUSER=admin
SET _RUNUSER=life
REM
IF NOT EXIST "%_CRYPTDRIVE%" (
CALL :SPEAK "Crypt Not Mounted"
AT|FOR /F "tokens=1" %%i IN ('FIND /I "%~f0"') DO AT %%i /delete /yes >nul
2>&1
pskill.exe -t putty >nul 2>&1
pskill.exe -t thunderbird >nul 2>&1
pskill.exe -t ThunderbirdPortable >nul 2>&1
pskill.exe -t Firefox >nul 2>&1
pskill.exe -t FirefoxPortable >nul 2>&1
pskill.exe -t GC >nul 2>&1
EXIT
)
REM
=============================================================================
==========
REM = SPEAK - Speak text
REM
=============================================================================
==========
:SPEAK
nircmd.exe speak text "%~1" 5 60
ECHO "%~1"
EXIT /B
REM
=============================================================================
==========
REM = RESTARTPLINK - Sleeps for %1 number of seconds
REM
=============================================================================
==========
:RESTARTPLINK
CALL :SPEAK "%~1 ReSTARTing Plink"
REM runas /savecred /user:admin "%_PUTTYBIN% -load %_PSESSION%"
psexec.exe -i 0 -e -d -u %_RUNUSER% %_PUTTYBIN% -load %_PSESSION%
EXIT
EXIT /B
REM
=============================================================================
==========
REM = PORTCHECK - Check that Port is being used (for tunnels)
REM
=============================================================================
==========
:PORTCHECK
netstat.exe -n -v -a -p TCP | FIND "ESTABLISHED" | FIND ":%~1" >nul 2>&1
IF ERRORLEVEL 1 CALL :RESTARTPLINK "PORT CHECK FAILED"
EXIT /B
REM
=============================================================================
==========
REM = INACTIVEPUTTYCHECK - Check for inactive putty windows
REM
=============================================================================
==========
:PUTTYCHECK
tasklist.exe /V /NH /FI "WINDOWTITLE eq PuTTY (inactive)" 2>nul|FIND "INFO: "
>nul 2>&1
IF ERRORLEVEL 1 taskkill.exe /T /F /FI "WINDOWTITLE eq PuTTY (inactive)" >nul
2>&1
:: MAKE SURE ONLY 1 putty is running that is connected to the remote server
netstat.exe -n -a -o -p TCP | FIND ":%_PPORT%" | FIND /C ":%_PPORT%" | FIND
"1" >nul 2>&1
IF ERRORLEVEL 1 (
tasklist.exe /V /FO TABLE /NH /FI "IMAGENAME eq putty.exe" 2>nul | FIND /C
"Running" | FIND "1" >nul 2>&1
IF ERRORLEVEL 1 (
REM kill all running puttys (owned by system)
FOR /F "usebackq tokens=2 skip=2" %%p IN (`tasklist.exe /V /FO TABLE /NH
/FI "IMAGENAME eq putty.exe"`) DO taskkill.exe /F /PID %%p /T >nul 2>&1
CALL :RESTARTPLINK "EXTRA PUTTY FOUND"
)
)
EXIT /B
REM
=============================================================================
==========
REM = PINGCHECK - PING address to make sure it is reachable
REM
=============================================================================
==========
:PINGCHECK
PING %~1 -n 1 -w 5000 | FIND "TTL=" >nul 2>&1
IF ERRORLEVEL 1 (
PING google.com -n 1 -w 500 | FIND "TTL=" >nul 2>&1
IF ERRORLEVEL 1 ( CALL :SPEAK "CHECK INTERNET CONNECTION" && SLEEP 60 )
REM
=============================================================================
==========
REM = KILLDUPES - kills duplicate processes, except for the one with lowest
pid
REM
=============================================================================
==========
:KILLDUPES
REM Check that more than 1 process is running
tasklist.exe /V /NH /FI "IMAGENAME eq %~1" /FI "USERNAME eq SYSTEM" 2>nul |
FIND /C "K Running" | FIND "1" >nul 2>&1
IF NOT ERRORLEVEL 1 EXIT /B
REM Create the file in the same dir as this script named for date and sorted
by PID
tasklist.exe /V /NH /FI "IMAGENAME eq %~1" /FI "USERNAME eq SYSTEM" 2>nul |
SORT /+29 > "%_T%"
REM kill all the processes found except for 1, do not kill the process with
the lowest pid number
FOR /F "tokens=2 skip=2" %%p IN (%_T%) DO taskkill.exe /F /PID %%p /T
EXIT /B
REM
=============================================================================
==========
REM = CREATEATJOB - runs job (START this file) every 5 minutes
REM
=============================================================================
==========
:CREATEATJOB
:: delete all putty AT jobs
AT | FIND /C "%~f0" | FIND "1" >nul 2>&1
IF ERRORLEVEL 1 ( AT|FOR /F "tokens=1" %%i IN ('FIND /I "%~f0"') DO AT %%i
/delete /yes >nul 2>&1 )
SET /A H=!TIME:~0,2!&& SET M=!TIME:~3,2!
SET Y=%H%:%M%&& SET /A M+=5
IF !M! GEQ 61 ( SET /A H+=1&&SET /A M-=61 )
IF !H! GEQ 24 SET /A H-=24
SET M=0!M!&& SET H=0!H!
:: create AT job
AT %H:~-2%:%M:~-2% /INTERACTIVE %ComSpec% /E:ON /D /Q /C START /B /MIN
%ComSpec% /E:ON /D /Q /C "%~f0" >nul
EXIT /B
Advanced Batch File ^
This is the main batch file example which you can download here. Other than some minor
changes this is the actual script I use at work when I logon to my PC. The first thing it does is
mount an encrypted TrueCrypt Drive where all of my files and settings are located. It also starts
a putty session named "1" that I configured to start a few encrypted tunnels and socks proxies so
that my email Thunderbird and Website IDE Dreamweaver and other network apps can
communicate 100% encrypted and my real location becomes hidden (thanks socks!).
I might come back later and add comments if I get any kind of response for this article, and
because it's such a unique and low-traffic topic, I will try to answer any questions added with the
comment form.
SCRIPT VARIABLES ^
These are all local to this script thanks to the SETLOCAL above, so they won't exist outside the
scripts execution environment.
REM ** runuser is the username you use when running this script
SET RUNUSER=bill
:: Only allow certain users to run this script or die (prevents global
STARTup or service running it)
CALL :CHECKUSERVALID %RUNUSER% %ADMINUSER%
Exit Script ^
This is the last line executed in the Main, it forces the script to exit cleanly at this point,
otherwise the functions below would all get executed. This is what allows the use of all the
functions below. I end all my scripts MAIN with this.
:: checks that MOUNTVOL works and the drive containing the truecrypt
container file is present or dies
MOUNTVOL %VOL% /L 2>NUL | FIND "\?Volume{" >NUL 2>&1
IF ERRORLEVEL 1 CALL :MDYE "%VOL% NOT FOUND"
RUNONE - Starts one instance of executable after verifying it exists and is not
already running. ^
REM %~1 is location of executatable
REM %~2 is optional (unless %~3 is used) START parameters
REM %~3 is optional parameters for executable
REM
REM CALL :RUNONE "L:PLPLightscreenPortable.exe" "/MAX" "/HIDE"
:RUNONE
SETLOCAL
CALL :MP 1 "Starting %~n1"
SET P=%~1
ECHO %P%|FIND " " >NUL 2>&1
IF NOT ERRORLEVEL 1 ( PUSHD "%~dp1" && SET P=%~nx1 )
SETLOCAL
SET UP=no
SET _P1= %~1
SET _P2= %~2
IF NOT "%_P1%" == " " ( IF /I "%~1" == "%USERNAME%" SET UP=yes)
IF NOT "%_P1%" == " " ( IF /I "%~1" == "%USERNAME%" SET UP=yes)
IF NOT "%_P1%" == " " ( IF /I "%~1" == "%USERNAME%" SET UP=yes)
IF /I "bill" == "%USERNAME%" SET UP=yes
IF /I "newbill" == "%USERNAME%" SET UP=yes
IF /I "max" == "%USERNAME%" SET UP=yes
IF /I NOT "%UP%" == "yes" EXIT
ENDLOCAL
EXIT /B
The character after the echo is the actual BEL char, so unless you have my source file, you will
need to copy a literal BEL char here to make it beep.
:BEEP
@ECHO # && EXIT /B
MSETCONSOLE - sets the cols and lines of current screen buffer, then returns
to CALLer ^
:MSETCONSOLE
MODE CON COLS=%~1 LINES=%~2 && EXIT /B
REM SORTABLES
IF /I "%_P%" == "pid" ( tasklist.exe /V /NH | SORT /+29 && EXIT /B )
IF /I "%_P%" == "size" ( tasklist.exe /V /NH | SORT /+59 && EXIT /B )
IF /I "%_P%" == "user" ( tasklist.exe /V /NH | SORT /+89 && EXIT /B )
IF /I "%_P%" == "time" ( tasklist.exe /V /NH | SORT /+138 && EXIT /B )
IF /I "%_P%" == "window" ( tasklist.exe /V /NH | SORT /+152 && EXIT /B )
REM FILTERS
IF /I "%_P%" == "image" ( tasklist.exe /V /NH /FI "IMAGENAME eq %~2" && EXIT
/B )
IF /I "%_P%" == "username" ( tasklist.exe /V /NH /FI "USERNAME eq %~2" &&
EXIT /B )
IF /I "%_P%" == "running" ( tasklist.exe /V /NH /FI "STATUS eq Running" &&
EXIT /B )
IF /I "%_P%" == "status" ( tasklist.exe /V /NH /FI "STATUS eq %~2" && EXIT /B
)
CALL :MF
EXIT /B
MM - prints blank line, L1, changes title of the interpreter window to %~1,
prints >>> %~1..., L2, blank line, then returns to CALLer ^
:MM
SLEEP 1 && ECHO. && ECHO %L1% && title +++ %~1... && ECHO %P1% %~1... && ECHO
%L2% && ECHO. && EXIT /B
MT - prints blank line, L1, changes title of the interpreter window to %~1, prints
>>> %~1..., L2, blank line, then returns to CALLer ^
:MT
CALL :MM "%~1" && CALL :SPEAK "%~1" && EXIT /B
MP3 - ECHO %~1, speak %~1 with nircmd.exe, then returns to CALLer ^
:MP3
CALL :MP 1 "%~1" && CALL :SPEAK "%~1" && EXIT /B
EOF: Thoughts ^
So what did you think? I have around 20 batch scripts that utilize these and other functions to do
all sorts of cool things. One takes a screenshot of my desktop every 10 minutes and saves it for a
real-cool archive of my activity. Another lets me edit a boot.ini file with 1 command.. And
another runs when I insert a USB drive to automatically mount a truecrypt volume and create
SSH tunnels in the background by using Plink, AT, and the runas.exe command.
If you want to program, please use linux... If you need to write a Windows batch file, I hope this
helps.