Thursday, December 07, 2006

String Length with Windows Batch

I've often found myself needing a reliable way to determine string length with windows batch files. I know there are probably add-ons or third-party solutions, but I prefer to do things with the base system whenever possible. Since I wasn't able to find any solutions, I figured I'd contribute one now that I've written it. This is my code, I don't care if you use it or what you use it for. I hope it works for you and if not, sorry. It relies on the set /a command, which I believe is exclusive to XP (it might work in 2000 but I no longer have a box to test it on.)

getstrlen.bat:
@echo off
REM Sets the variable "strlen" to the length of the first argument.
REM
REM Usage:
REM getstrlen

if "%1"=="" (echo getstrlen error: no argument &goto lengthset)
if not "%2"=="" (echo getstrlen error: too many arguments &goto lengthset)

set count=1
set strlen=

:checklen
echo>%temp%\checker.bat set STRING=%1
echo>>%temp%\checker.bat set equal=no
echo>>%temp%\checker.bat set CHECK=%%STRING:~-%count%%%
echo>>%temp%\checker.bat if "%%STRING%%"=="%%CHECK%%" (set equal=yes)
call %temp%\checker.bat
del %temp%\checker.bat
if "%equal%"=="yes" (set strlen=%count% & goto lengthset)
set /a count="count+1"
if not "%count%"=="256" goto checklen
set strlen=GE256
:lengthset

This will set a session variable called "strlen" which will be assigned the numerical length of whatever string you pass to getstrlen.bat.

Example:
c:\>getstrlen.bat Badger
c:\>echo %strlen%
6
c:
How it works:
The :checklen marker is the beginning of a loop that increments an iteration variable called count.

Every time through the loop the variable passed to the batch is checked by string substitution. The logic looks at a number of characters (represented by the count variable) and compares that to the string itself.

For example, if the string being checked was "Badger", the first time through the loop "Badger" (represented by the variable STRING) would be checked against "B" (represented by the variable CHECK) because count is set to 1. Since they don't match, the script will increment count by 1 and jump back up to :checklen. This time it will check "Badger" against "Ba" because count is set to 2. The process repeats until the sixth time around when both CHECK and STRING are equal to "Badger" at that point count is set to 6. When the helper.bat script finds that the two are equal the value of check is assigned to strlen and the script exits.

Limitations:
The script will only verify strings up to 255 characters long (hopefully you're not trying to use Windows batch for stuff bigger than that!). If you try to check a string longer than that the value of strlen will be set to "GE256" meaning "Greater or equal to 256". And the script will blow up if you try to pass invalid characters like spaces, semicolons or ampersands. Some punctuation is okay but I don't have the time to write an exhaustive list. A safe bet is to either only use this script on strings composed of letters and numbers or to build in your own string validation logic.


What's the point of the checker.bat?
Because variable refrencing in Windows Batch is so limited it is not ordinarily possible to use variables with the string substitution functions of the set command.

I can't do this -
set CHECK=%STRING:~-%count%%
Because the set command will not evaluate %count% before attempting to use it.

In order to get around this I dynamically construct a secondary batch (helper.bat) file using echo statements.
%temp%\checker.bat set CHECK=%%STRING:~-%count%%%
Because the echo command will resolve the variable "count" to the value it holds, the echoed statement is perfectly valid.

If "count" happened to equal 3 during the previous command, open up checker.bat and you'll see:
set CHECK=%STRING:~-3%
Since this is using an actual number rather than a variable, we're all set. Now all that's needed is to call checker.bat from your original script and the variable is set as desired.

It's a bit silly to have to go so far out of your way just to modify the input of a command, but at least it's possible.

Let me know if you found this helpful!

No comments: