Thursday, October 18, 2007

Advanced Batch File Techniques

Many Windows administrators keep using batch for small scripts and utils. It is a good idea in the right use!

Batch is good to keep things simple, and the majority of your collegues most likely will feel safe when its in batch, compared to perl, vbs, php, sh or even c/c++ utils. Of course there are people who are so good at programming that they can read code, but most likely they are not your avarage Windows administrator collegue.

I found an advanced batch example, which uses functions in batch, and also writes to files, reads from files and cleanup of course. Its good for some batch education. Also check out the improved version, which does not use files. Impressive :-)

For my own little usage today, i wanted to get the drive from a string, so i can switch the that drive and directory, and at the same time replace / to \ so mkdir and cd works. Looking at the all round really cool scripting pages from Rob van der Woude, and his awesome batch examples, I came up with the test script which does the job:

@echo off
SET STRING=c:/some/dir
ECHO Original string: %STRING%
SET STRING=%STRING:/=\%
SET FIXEDSTRING=%STRING%
ECHO Fixed string: %FIXEDSTRING%

FOR /F "tokens=1 delims=\ " %%A IN ('echo %string%') DO SET drive=%%A
ECHO we are on %drive%

SET STRING=%STRING:~0,1%
ECHO or another way, we are on %drive%

SET STRING=%FIXEDSTRING:~2,9999%
ECHO Dir string: %STRING%

What remains would be how to suck in a config file, or a file with lines of strings that I want to manipulate (eg. a list of filenames). How can I do this? :-) Some places to look could be:

http://home.pcisys.net/~sungstad/useful/PCbatch.html

http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/for.mspx?mfr=true

http://www.maem.umr.edu/batch/stack1.html

UPDATE 1:

My first example broke if STRING had quotations, so instead I came up with this to get drive and dirname, and i also stripped quotes:

REM Find the drive of %localroot%:
FOR /F "tokens=1 delims=\ " %%A IN ('echo %localroot%') DO SET localrootdrive=%%~dA

REM Remove surrounding quotation marks:
FOR /F "delims=" %%A IN ('echo %localroot%') DO SET localroot=%%~A
FOR /F "delims=" %%A IN ('echo %vsspath%') DO SET vsspath=%%~A

REM Now get directory:
set localrootdir=%localroot:~2%

REM Strip any leading /
IF "%vsspath:~0,1%"=="/" SET vsspath=%vsspath:~1%
REM Replace / to \ in VSS path:
set vsspathwin=%vsspath:/=\%

REM Create a tmpfile we can use (this is from MKSNT, not Windows):
FOR /F "usebackq" %%A IN (`tempfile`) DO set envtmpfile=%%A
set envtmpfile=%envtmpfile:/=\%
if exist %envtmpfile% del %envtmpfile%

REM Exit if there was an error earlier, eg. like:
IF ERRORLEVEL 1 set error=true
if "%error%"=="true" exit /b 1
exit /b 0

Some for loop notes:
for /l %a in (start increment final) do
for /l %i in (4 2 10) do echo %i
To work with all lines from a .txtfile or output from a command use:
for /f %i in (c:\file1.txt c:\file2.txt) do echo %i
for /f %i in ('dir /ad /b \\server\share\files*') do echo %i

Remember to use %%i in scripts, and not just %i.

On a side note, do remember that environment variables can be usefull in your batch scripts. Set var=something and set var= to unset. And access them from inside your scripts as you would normal variables:
if defined SOME_ENV_VAR goto somewhere
start some.exe

UPDATE 2:

Turns out running call /? gives you much of the cool information i have missed during the years:

call /?
Calls one batch program from another.

CALL [drive:][path]filename [batch-parameters]

batch-parameters Specifies any command-line information required by the
batch program.

If Command Extensions are enabled CALL changes as follows:

CALL command now accepts labels as the target of the CALL. The syntax
is:

CALL :label arguments

A new batch file context is created with the specified arguments and
control is passed to the statement after the label specified. You must
"exit" twice by reaching the end of the batch script file twice. The
first time you read the end, control will return to just after the CALL
statement. The second time will exit the batch script. Type GOTO /?
for a description of the GOTO :EOF extension that will allow you to
"return" from a batch script.

In addition, expansion of batch script argument references (%0, %1,
etc.) have been changed as follows:


%* in a batch script refers to all the arguments (e.g. %1 %2 %3
%4 %5 ...)

Substitution of batch parameters (%n) has been enhanced. You can
now use the following optional syntax:

%~1 - expands %1 removing any surrounding quotes (")
%~f1 - expands %1 to a fully qualified path name
%~d1 - expands %1 to a drive letter only
%~p1 - expands %1 to a path only
%~n1 - expands %1 to a file name only
%~x1 - expands %1 to a file extension only
%~s1 - expanded path contains short names only
%~a1 - expands %1 to file attributes
%~t1 - expands %1 to date/time of file
%~z1 - expands %1 to size of file
%~$PATH:1 - searches the directories listed in the PATH
environment variable and expands %1 to the fully
qualified name of the first one found. If the
environment variable name is not defined or the
file is not found by the search, then this
modifier expands to the empty string

The modifiers can be combined to get compound results:

%~dp1 - expands %1 to a drive letter and path only
%~nx1 - expands %1 to a file name and extension only
%~dp$PATH:1 - searches the directories listed in the PATH
environment variable for %1 and expands to the
drive letter and path of the first one found.
%~ftza1 - expands %1 to a DIR like output line

In the above examples %1 and PATH can be replaced by other
valid values. The %~ syntax is terminated by a valid argument
number. The %~ modifiers may not be used with %*

A good explanation of the FOR LOOP possibilites can be found here [http://www.computerhope.com/forhlp.htm]:

eol=c
specifies an end of line comment character (just one)

skip=n
specifies the number of lines to skip at the beginning of the
file.

delims=xxx
specifies a delimiter set. This replaces the default delimiter
set of space and tab.

tokens=x,y,m-n
specifies which tokens from each line are to be passed to
the for body for each iteration. This will cause additional variable names to be
allocated. The m-n form is a range, specifying the mth through the nth tokens.
Ifthe last character in the tokens= string is an asterisk, then an additional
variable is allocated and receives the remaining text on the line after the last
token parsed.

usebackq
specifies that the new semantics are in force, where a back
quoted string is executed as a command and a single quoted string is a literal
string command and allows the use of double quotes to quote file names in
filenameset.


2 comments:

J said...

It been a while since I was here, but here is a useful site for Windows Command line information:

http://en.wikibooks.org/wiki/Guide_to_Windows_Commands

daspeac said...

I have heard about another way of file recovery for clipper. Besides, you can visit my blogs at: http://daspeac.livejournal.com/ or http://daspeac.blogspot.com/ where I’m trying to share my experience with regard to data corruption issues.