From ts@uwasa.fi Fri Mar 1 01:00:00 2002
Subject: Batchtricks file information
Date: Fri, 1 Mar 2002 01:00:00
From: ts@uwasa.fi (Timo Salmi)

Assorted Batch Tricks                               Fri 1-March-2002
=====================
                                                 All rights reserved
                               Copyright (c) 1993-2002 by Timo Salmi

....................................................................
Prof. Timo Salmi   Co-moderator of news:comp.archives.msdos.announce
Moderating at ftp:// & http://garbo.uwasa.fi/ archives 193.166.120.5
Department of Accounting and Business Finance  ; University of Vaasa
mailto:ts@uwasa.fi <http://www.uwasa.fi/~ts/>  ; FIN-65101,  Finland
Spam foiling in effect.  My email filter autoresponder will return a
required email password to users not yet in the privileges database.
....................................................................

  Ŀ
   This file belongs to TSBAT*.ZIP. Please do not distribute 
   this 1BATFAQ.TXT file separately! If you see this file    
   reproduced alone, please alert the SysAdmin immediately.  
  

Introduction
============

This file contains assorted batch tricks. The items are in no
particular order. Many, but not all, have been used in the
TSBAT*.ZIP collection of batches. Likewise, there are some useful
further tricks, not documented here, to be found in the TSBAT
batches.

Many users have sent me useful suggestions and their own alternative
or further solutions. My best thanks for the material. You can find
much of this feedback and the other users' solutions stored in the
ftp://garbo.uwasa.fi/pc/pd2/tspost00.zip files. The items by the
other users are listed at the end of tsbat.inf included in this
collection.

For further batch programming material via the World Wide Web
connect to http://garbo.uwasa.fi/pc/batchutil.html. Also, connect to
my home page http://www.uwasa.fi/~ts/ then click my collection of
HTTP links and find the section on programming.

You are free to quote brief passages from this 1BATFAQ.TXT file
provided you clearly indicate the source with a proper
acknowledgment.

Comments and corrections are solicited. But if you wish to have
individual batch programming consultation, please rather post your
question to a Usenet newsgroup like news:comp.os.msdos.programmer or
news:comp.os.msdos.misc. It is much more efficient than asking me by
email. I'd like to help, but I am very pressed for time. I prefer to
pick the questions I answer from the Usenet news. Thus I can answer
publicly at one go if I happen to have an answer. Besides,
newsgroups have a number of readers who might know a better or an
alternative answer. Also please see item #56. Don't be discouraged,
though, if you get a reply like this from me. I am always glad to
hear from fellow batch file users.

A special Windows 95/98/Me note: Some of the tricks in this
collection will not work, or will not work without adjustments, for
MS-DOS run in a Windows 95 (or later) box. One subtle pitfall I have
know of is that Windows 95 uses long file names while the
information in this collection is based on the 8+3 filename
convention. This will affect some of the batches. And so on. The
same goes all the more for Windows NT.

The author shall not be liable to the user for any direct, indirect
or consequential loss arising from the use of, or inability to use,
any program, batch or file howsoever caused. No warranty is given
that the batches, the programs or the advice given will work under
all circumstances.
--------------------------------------------------------------------

A recommendation list of the auxiliary programs to get for some of
the solution versions presented:

SED.EXE Stream Editor
 20368 Oct 1 1991 ftp://garbo.uwasa.fi/pc/unix/sed15x.zip
 HHSED executable, E.Raymond+D.Kirschbaum+H.Helman

GAWK.EXE A Pattern Scanning and Processing Language
 233923 Mar 25 1995 ftp://garbo.uwasa.fi/pc/unix/gawk2156.zip
 GNU awk text scanning and processing language
 (Note, use this older version, not the more recent ones!)

ECHON.EXE echo -n, Echo without a linefeed
 Included in this ftp://garbo.uwasa.fi/pc/link/tsbat.zip
 A collection of useful batch files and tricks, T.Salmi

CUT.EXE    Extract or delete text columns
CUTW.EXE   Extract or delete text by words
CONCAT.EXE Join two text-files side by side
LOWER.EXE  Convert to lower case incl spec
UPPER.EXE  Convert to upper case incl spec
 108902 Aug 18 2000 ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip
 More filters: dump,col,concat,cut,detab,rep,rot13,undump,...

FN.EXE FuNction evaluator
 77497 Oct 13 2001 ftp://garbo.uwasa.fi/pc/ts/tsfunc17.zip
 Calculates, tabulates, and plots your functions, T.Salmi

WHATDATE.EXE Date +- number of days from now
 111711 Oct 23 2001 ftp://garbo.uwasa.fi/pc/ts/tsutlc26.zip
 tsutlc26.zip Timo's 3rd utility set (dirf,dirinfo,doubles,hidden,split,...)
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:01 2002
Subject: Batch tricks index
Date: Fri, 1 Mar 2002 01:00:01
From: ts@uwasa.fi (Timo Salmi)

INDEX
=====

1)  How can I make "@echo off" MS-DOS version independent?
2)  Deleting all files without being prompted "Are you sure (Y/N)?"
3)  Is it possible to nest for loops in batch files?
4)  How can I check in a batch whether a directory exists?
5)  Checking that a program is available at the current directory or at path
6)  Is it possible to use subroutines and recursion in batches?
7)  How can I convert a lowercase parameter to uppercase?
8)  Is there an easy way to append a new directory to the path?
9)  How can I compare if two files are identical?
10) Writing an empty line to the screen. How is that done?
11) A tip: Customizing the pause message
12) A tip: Complicated renaming of files with for
13) How can I check if the file name given included wildcards?
14) Is it possible to prevent the user from breaking the batch?
15) Can I prevent a break from bypassing the autoexec.bat?
16) How can I extract the extension of a file name?
17) Information on the quote character %
18) Eliminating auxiliary batches (making do with the main batch)
19) Utilizing the subst command in paths
20) How can I run a batch once a week (testing for the weekday)
21) How can I test if a given file name includes the path?
22) How can I display the time without having to press enter?
23) Alternatives for testing for the errorlevel value
24) About redirecting a batch file's output
25) How can I test if my batch has sufficient environment space?
26) Is there a simple trick to "disable" or hide a drive?
27) How can I send an escape sequence to the printer?
28) Is it possible to create a random string in a batch?
29) Is it possible in a batch to find out the length of a string?
30) How to obtain the MS-DOS version into an environment variable?
31) How can I find out the number of regular files on a drive?
32) How can I use a batch to augment line numbers to my text file?
33) Storing and returning to the original directory (push and pop)
34) Enticing the current date into an environment variable
35) A tip for power users. Identifying the individual PC.
36) For loop and redirection quirks
37) Is it possible to traverse a directory tree with a batch?
38) Is it possible to echo the redirection symbol in a batch?
39) How can I extract the file basename?
40) A batch to put user input into an environment variable
41) How can I get the last replaceable parameter given to a batch?
42) Creating an empty file if the file does not already exist
43) How can I change or remove the disk volume serial number?
44) How to pause in a batch for a preset number of seconds?
45) Where can I find a program to compile batches into COMs or EXEs?
46) How can I test whether a disk is empty or not?
47) How can I run a different batch depending on the weekday?
48) Can one put line numbers into a file with just batch commands?
49) How can I backup from the current directory files made today?
50) How can I traverse all files of a directory in a batch?
51) How can I step through a batch a command at a time to debug it?
52) How to display all files made or updated on a day or today?
53) How can I make a list of all my files and locate a certain file?
54) How can I tell if a batch is running in a Windows dosbox?
55) How can I test if there is a disk in a floppy disk drive?
56) Could you please solve this problem for me with a batch?
57) How can I make a loop that is repeated a preset number of times?
58) How can I display the contents of the memory?
59) How get today's date elements into environment variables?
60) How can I find and copy the files updated today in a directory?
61) How can I test in a batch whether a TSR program has been loaded?
62) Putting the current drive letter into an environment variable.
63) How can I extract the drive letter from a full file path?
64) How can I write a "SLEEP" command to pause for a certain time?
65) How can put comments into a batch file?
66) How can I echo just the word "off" in a batch file?
67) How can I extract the first two characters of a file name?
68) How can I compare two numbers with batch commands?
69) All these solutions are for wimps. Why not rather use 4DOS?
70) How can I give more than the nine parameters to a batch?
71) How can I update copy files from one directory to another?
72) How can I best clear all the files from my TEMP directory?
73) How can I check the number of parameters given to a batch?
74) How can I locate and e.g. delete all *.TMP files on a drive?
75) How can I rename all files sequentially in a directory?
76) How to search all the *.txt files on a drive for a word?
77) How can I give multiple commands on one line?
78) Is there a batch to individually zip each file in a directory?
79) A batch to substitute a string through a set of text files?
80) Where do I find a good book or WWW page on batch programming?
81) How to read a file's date and time into environment variables?
82) How would I get a beep in a batch file?
83) Y2K: My old BIOS gives a wrong year at boot. How can I fix it?
84) Is there a batch to drop the first letter of a file name?
85) How do I close the command window once my batch file has run?
86) Is MS-DOS batch programming going to die along with W2K?
87) How can I test for the existence of a substring in a string?
88) How can I put MS-DOS FIND results into an environment variable?
89) How to get months as letters and years in four digits in DIR?
90) Isn't MS-DOS dead with W2K? So why bother with MS-DOS batches?
91) How can I exit the called batch without returning to the first?
92) How do I create a sequence of directories from 1 to MaxValue?
93) How do I add +1 to a variable in a batch?
94) How to copy the errorlevel into a variable in a batch?
95) How can I test for a zero byte file? Or against a larger size?
96) How to show the last N lines of a file using DOS batch files?
97) How do I get the path of a file located somewhere on my disk?
98) How to read a file's attributes into environment variables?
99) Is it possible to echo without linefeed like the Unix echo -n?
100) How can I detect the F1-F10 function keys in a batch?
101) How can I get the free size and total size of a drive?
102) How can I search for a string through a directory of files?
103) How do I find the largest numeric extension and add +1 to it?
104) How do I run the latest executable from the default directory?
105) How can I determine if ANSI.SYS is loaded?
106) How do I get the last drive into an environment variable?
107) How can I calculate the number of lines in a file?
108) Is it possible to save and later restore the command history?
109) Is it possible to change just one line of a file with a batch?
110) Is it possible to delete lines by their line numbers only?
111) How can I tell what drive has been allocated by ramdrive.sys?
112) How can in insert a line in the middle of a file with a batch?
113) What is difference between MS-DOS and various Windows batches?
114) Is it possible to display comments from the config.sys file?
115) Can I split a single command over multiple lines in a batch?
116) How can I get yesterday's date into an environment variable?
117) How do I get the 3rd word from the 5th line of a text file?
118) How to put the content of text file into a variable?
119) How do I get the three oldest and latest files of a directory?
120) Hint: Displaying an ASCII (hex) value table
121) How to make %0 include the full path of the called batch file?
122) How can I test if a file is dated less than hour ago?
123) How I can (re)set the screen text and background colors?
124) How do I get the echo status into an environment variable?
125) How can I delete all empty directories on a drive?
126) How can I put an = into an environment variable?
127) How can I reverse a word or an entire line?
128) How can I add date and time to each line of a text file?
129) Is a column-sensitive search possible through a text file?
130) How can I calculate the number of words in a text file?
131) How can I put the lines of a text file into a reverse order?
132) How can I add 5:23:11 to the current time?
133) How do I find out the number of parameters given to my batch?
134) How to remove the first two characters throughout my text file?
135) Can one calculate the difference between two times in a batch?
136) Is it safe to use debug or other similar tools in batches?
137) How can I retrieve a keyword from a text file into environment?
138) How to find if there is an identical file elsewhere on my HD?
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:02 2002
Subject: Generalizing @echo off
Date: Fri, 1 Mar 2002 01:00:02
From: ts@uwasa.fi (Timo Salmi)

1. How can I make "@echo off" MS-DOS version independent?
=========================================================

If you want to turn the echo off, and do not wish to show that line
on the screen, you can easily do this by applying
 @echo off

There is a catch, however, because this only works since MS-DOS
version 3.30. So if you want to make it general, put the following
line in your autoexec.bat file if you are using MS-DOS 3.30 or
higher
 set _echo=@
Then use the following format in your batches, which will then work
for any MS-DOS version
 %_echo%echo off
Of course, many if not most users will have a much more recent
MS-DOS version. As we know, the last released independent MS-DOS is
6.22. After that Microsoft has been pushing Windows and trying to
kill its MS-DOS despite the huge user base still left in the world.
   How does one find out which MS-DOS version one has? The version
is, of course, given by the VER command. For using this in testing
in a batch see the later item #30 "How to obtain the MS-DOS version
into an environment variable?"
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:03 2002
Subject: Deleting all files
Date: Fri, 1 Mar 2002 01:00:03
From: ts@uwasa.fi (Timo Salmi)

2. Deleting all files without being prompted
============================================

One of the most Frequently Asked Questions (FAQs) about batches is
how to suppress the "Are you sure (Y/N)?" confirmation requirement
for del *.*.  Use the following:
 echo y| del *.*
If you wish to suppress the message too, use
 echo y| del *.* > nul
There is also another alternative for doing this:
 for %%f in (*.*) do del %%f
If the directory is empty you can avoid the "File not found" message
by applying
 if exist *.* echo y| del *.* > nul
A better alternative by Rik D'haveloose:
  if exist *.* for %%f in (*.*) do del %%f
Whether or not it is sensible to suppress the confirmation can be
debated, but these are the tricks anyway. Even more dangerous is
using such a trick on DELTREE or FORMAT. I do not recommend it. In
fact it is better to disable these two commands (so that they will
require a full path, as given in 1DOSTRIK.TXT item "7. Selected
Doskey macro examples". Or, alternatively, one can move them to a
hidden directory, out of the path. (See HELP ATTRIB for making a
dirctory hidden.)
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:04 2002
Subject: Nested for loops
Date: Fri, 1 Mar 2002 01:00:04
From: ts@uwasa.fi (Timo Salmi)

3. Is it possible to nest for loops in batch files?
===================================================

Yes, it is possible to have nested loops of a kind in batch
programming, but not directly. You have to call a second batch file.
Consider the following two batches, and try it out by calling
test.bat.

  @echo off
  rem TEST.BAT
  for %%f in (a b c d e f) do %comspec% /c test2 %%f

  @echo off
  rem TEST2.BAT
  echo.
  for %%g in (1 2 3) do echo %1%%g

   In the above "%comspec%" has been used for greater generality
across MS-DOS versions. Alternatively you could have
  @echo off
  rem TEST.BAT
  for %%f in (a b c d e f) do call test2 %%f
In TEST2.BAT the line "echo." is only for better readability of the
output.
   It is also possible to use only one batch file by letting the
first batch create the second
  @echo off
  rem TEST.BAT
  ::
  echo @echo off > test$$$2.bat
  echo echo. >> test$$$2.bat
  echo for %%%%g in (1 2 3) do echo %%1%%%%g >> test$$$2.bat
  ::
  for %%f in (a b c d e f) do call test$$$2 %%f
  if exist test$$$2.bat del test$$$2.bat
Note the need of the extra "%":s in echoing from a batch file to
another.

Another alternative is to write everything as below on a *SINGLE*
line:
  for %%f in (a b c d e f) do %comspec% /c
    for %%g in (1 2 3) do echo %%f%%g
(The wrap has been used in the text is because of the right margin.
Don't wrap your batch.). The disadvantage of this alternative is
that the echo will be on.

In the above you may wonder about the "%COMSPEC% /C" alternative to
"CALL". The comspec is and environment variable that points to the
location of the COMMAND.COM command interpreter. The /C switch tells
that the command interpreter is to exit after performing the
specified line. The /C switch must be the last switch on the command
line. The "%COMSPEC% /C" alternative is available in earlier
versions of MS-DOS than the CALL command.

Another option is to use third party programs and utilize a unix
port like G(nu)Awk. Below is an awk-based solution
  @echo off
  ::
  :: Initialize and run the outer loop from 1 to 5.
  set i_=0
  :_loop
    echo %i_%|gawk '{printf "@set i_=%%s\n",$1+=1}'>tmp###.bat
    call tmp###.bat
    :: The inner loop with a for loop (could be "gawked", as well)
    for %%f in (1 2 3) do echo %i_% %%f
  if "%i_%"=="5" goto _out
  goto _loop
  ::
  :: Clean up
  :_out
  set i_=
  for %%f in (tmp###.bat) do if exist %%f del %%f

Within GAWK programming solutions like the one below are relatively
easy to write:
  @echo off
  >  tmp$$$.awk echo {
  >> tmp$$$.awk echo for (i=1;i!=6;i++)
  >> tmp$$$.awk echo   for (j=1;j!=4;j++)
  >> tmp$$$.awk echo     printf "i=%%s j=%%s\n",i,j
  >> tmp$$$.awk echo }
  ::
  echo.|gawk -f tmp$$$.awk
  ::
  for %%f in (tmp$$$.awk) do if exist %%f del %%f

A nested loop using recursive calling is useful for several tasks.
Consider an example problem of echoing sequentially the numbers from
01 to 99. The solution is given below. If you wish to start from
"00" rather than "01", then delete the line: if "%x_%"=="00" goto
_out. The format initiates from an appreciated posting by Herbert
Kleebauer but the initial idea dates back at least to John Savage
"Subject: a novel counter, Date: 1996/07/03 Message-ID:
<4rdq1s$5uo$1@sydney.DIALix.oz.au>."
  @echo off
  if not "%2"=="" goto _subru
  for %%i in (0 1 2 3 4 5 6 7 8 9) do call %0 %1 %%i
  goto _out
  :_subru
  set x_=%1%2
  if "%x_%"=="00" goto _out
  echo %x_%
  :_out
Another case. What if you want a loop from "000" to "255"?
  @echo off
  if "%exit_%"=="yes" goto _out
  if not "%3"=="" goto _subru
  for %%i in (0 1 2 3 4 5 6 7 8 9) do call %0 %1 %2 %%i
  goto _out
  :_subru
  set x_=%1%2%3
  echo %x_%
  if "%x_%"=="255" set exit_=yes
  :_out

Yet another method and example to build nested loops in batches.
This time without any 'for's
  @echo off
  ::
  :: Initialize
  set i_=0
  :: The outer loop (run from 1 to 5)
  :_loop_i
    ::
    :: Use FN.EXE
    :: to increase the variable
    fn /e %i_%+1 0 > nul
    set i_=%fn_%
    ::
    :: The inner loop (run from 1 to 3)
    set j_=0
    :_loop_j
      fn /e %j_%+1 0 > nul
      set j_=%fn_%
      :: Perform whatever
      echo i_=%i_% j_=%j_%
      ::
      if "%j_%"=="3" goto _next_i
    goto _loop_j
  :_next_i
  if "%i_%"=="5" goto _end_i
  goto _loop_i
  :_end_i
  ::
  :: Clean up
  for %%v in (i_ j_ fn_) do set %%v=

With the above method, an alternative solution to Herbert's trick
would be
  @echo off
  :: Initialize
  set i_=-1
  set z_=00%  Set leading zeroes%
  :_loop_i
    ::
    :: Use FN.EXE
    :: to increase the variable
    fn /e %i_%+1 0 > nul
    set i_=%fn_%
    if "%i_%"=="10" set z_=0
    if "%i_%"=="100" set z_=
    echo %z_%%i_%
  :_next_i
  if "%i_%"=="255" goto _end_i
  goto _loop_i
  :_end_i
  ::
  :: Clean up
  for %%v in (i_ z_ fn_) do set %%v=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:05 2002
Subject: Checking directory existence
Date: Fri, 1 Mar 2002 01:00:05
From: ts@uwasa.fi (Timo Salmi)

4. How can I check in a batch whether a directory exists?
=========================================================

It is sometimes useful to be able to test whether a particular
directory exists. The following test is true if the %1 directory
does not exist.
 if not exist %1\nul if not exist %1nul echo Directory %1 does not exist

However, this does not seem to work for a CD-ROM. Probably because
of the CD-ROM drivers use a slightly different directory system with
no . (dot) and .. (dot-dot). My thanks to Bjorn Svensson for
bringing this to my attention. A solution that works also for
CR-ROMs is
  @echo off
  if "%1"=="" goto _usage
  set target_=%1
  ctty nul
  dir /ad  %target_% | find " file(s) "
  ctty con
  if errorlevel==2 goto _1
  if errorlevel==1 goto _1
  echo Directory %target_% exists
  goto _out
  :_1
  echo Directory %target_% does not exist
  goto _out
  :_usage
  echo Usage %0 DirectoryName
  :_out
  set target_=
The "ctty nul/ctty con" command pair suppresses displaying the "File
not found" error message if the directory does not exist. Be careful
to get the "ctty nul/ctty con" pair right lest you lock your system.

Another, very similar but independently developed method of my own
(un)doing for testing directory existence
  @echo off
  ::
  if "%1"=="" goto _usage
  ::
  dir %1|find " Directory of "|find /i "%1">nul
  set found_=yes
  if errorlevel==2 set found_=no
  if errorlevel==1 set found_=no
  ::
  if "%found_%"=="yes" echo %1 is a directory
  if "%found_%"=="no" echo %1 is not a directory
  goto _end
  ::
  :_usage
  echo Usage: %0 [Target]
  ::
  :_end

Furthermore in the "if not exist %1\nul" method problems are caused
by the Windows dosbox / MS-DOS versions for Windows. Although this
FAQ-type file has been written for vanilla MS-DOS let's consider yet
another alternative that does not rely on the \nul trick. One way of
testing the existence of a directory is trying to write a file to
it, and testing if the file then exists. Obviously this trick can't
be used for read-only devices like CD-ROMs and write protected
diskettes. On the other hand exactly that is the usefulness of this
method if the intention is to write something.
  @echo off
  if "%1"=="" goto _help
  ctty nul
  echo.>%1\tmp#$#$#
  ctty con
  if exist %1\tmp#$#$# echo Directory %1\ exists
  if not exist %1\tmp#$#$# echo Directory %1\ does not exist
  if exist %1\tmp#$#$# del %1\tmp#$#$#
  goto _end
  :_help
  echo Usage %0 [DirectoryName]
  echo No backslash. Root e.g. as C:
  echo A subdirectory e.g. as C:\TMP
  :_end
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:06 2002
Subject: Checking program existence
Date: Fri, 1 Mar 2002 01:00:06
From: ts@uwasa.fi (Timo Salmi)

5. Checking that a program is available at the current directory or at path
===========================================================================

When you call a program from a batch, and do not give the explicit
path to it, it is advisable to test that the program is available
either at the current directory or the default path.
  set _found=
  if exist %1 set _found=yes
  for %%d in (%path%) do if exist %%d\%1 set _found=yes
  for %%d in (%path%) do if exist %%d%1 set _found=yes
  if "%_found%"=="yes" goto _continue
  echo %1 is not at path or the current directory
  goto _out
  :_continue
  echo %1 found at path or in the current directory
  :_out
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:07 2002
Subject: Subroutines and recursion
Date: Fri, 1 Mar 2002 01:00:07
From: ts@uwasa.fi (Timo Salmi)

6. Is it possible to use subroutines and recursion in batches?
==============================================================

Yes, it is possible to use subroutines within batches. The crucial
trick is setting an environment variable (e.g. _return) to point to
a label where to return after the subroutine has been performed. For
an example see UNPACK.BAT, and BOOT.BAT, the sections :_common and
:_subru.

Likewise it is possible to use recursion to emulate subroutines in
batches. (Recursion means that a batch calls itself).
As an example see SAFEDEL.BAT and trace the effects of the line
 for %%f in (%1) do call safedel %%f recurse
Note that safedel could be replaced by %0 because the zeroth
parameter of a batch file points to itself.

Besides the material referred to in the above, below is very simple,
artificial demonstration example of using a subroutine within a
batch file.
  @echo off
  set name_=FirstName
  set return_=_label1
  goto _subru
  :_label1
  ::
  set name_=SecondName
  set return_=_label2
  goto _subru
  :_label2
  echo Done!
  goto _end
  ::
  :_subru
  echo %name_%
  goto %return_%
  ::
  :_end
  set name_=
  set return_=
Below is the same demonstration using one-level recursion. As you
see, this option is more brief, since no accounting is needed to
keep track of the return points.
  @echo off
  if "%1"=="recurse" goto _recurse
  ::
  set name_=FirstName
  call %0 recurse
  ::
  set name_=SecondName
  call %0 recurse
  ::
  echo Done!
  goto _end
  ::
  :_recurse
  echo %name_%
  ::
  :_end
For further examples browse items #3, #23, #59, #71 and #79.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:08 2002
Subject: Conversion to uppercase
Date: Fri, 1 Mar 2002 01:00:08
From: ts@uwasa.fi (Timo Salmi)

7. How can I convert a lowercase parameter to uppercase?
========================================================

This example shows how to ensure that the parameter %1 given to the
batch is in uppercase. This utilizes the fact that MS-DOS converts
the path to uppercase. The result is stored in upcase_ and then the
original path is restored.
  set tmp_=%path%
  path=%1
  set upcase_=%path%
  path=%tmp_%
  set tmp_=

The also is another method for getting case-independent results.
This is adapted from Jeff Prosise's column in PC Computing, March
1993, pp. 216-217. If the batch below is called TEST.BAT, it makes
no difference whether you enter "TEST yes" or "TEST YES" or "TEST
yEs".
  @echo off
  if not "%1"=="" set %1=*****
  set status_=
  if "%yes%"=="*****" set status_=yes
  if "%no%"=="*****" set status_=no
  if not "%status_%"=="" echo The parameter %%1 was a %status_%
  if "%status_%"=="" echo The parameter %%1 was neither a yes nor a no
  if not "%1"=="" set %1=

The third option to convert a word into upper case is to utilize a
higher level language, such as the free version of Turbo Pascal,
which is available on the net (see item #100) First write and
compile the following Turbo Pascal program to make an UPPER.EXE
batch enhancer:
  program upper;
  var  s : string;
       i : byte;
       f : text;
  begin
    if ParamCount > 0 then s := ParamStr(1) else s := '';
    for i := 1 to Length(s) do s[i] := UpCase(s[i]);
    Assign (f, 'tmp#$#$#.bat');
    Rewrite (f);
    writeln (f, '@echo off');
    writeln (f, 'set upcase_=',s);
    Close (f);
  end.
Then apply e.g. the following test batch.
  @echo off
  ::
  :: Call the environment variable setting Turbo Pascal program
  upper %1
  ::
  :: Call the batch written by the Turbo Pascal program
  call tmp#$#$#
  ::
  :: Show the results
  if "%1"=="" echo: Usage %0 String
  if not "%1"=="" echo upcase_=%upcase_%
  ::
  :: Clean up
  del tmp#$#$#.bat
  set upcase_=
The example also goes to show how you can fairly easily write batch
enhancers yourself.

The fourth option is utilizing QBASIC which comes with MS-DOS 6.22.
In this option the program is embedded within the batchfile. The
batch file creates the program needed.
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  >  tmp$$$.bas echo LET a$ = "%1"
  >> tmp$$$.bas echo LET b$ = "abcdefghijklmnopqrstuvwxyz"
  >> tmp$$$.bas echo LET c$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  >> tmp$$$.bas echo FOR I = 1 TO LEN(a$)
  >> tmp$$$.bas echo   LET J = INSTR(b$, MID$(a$, I, 1))
  >> tmp$$$.bas echo   IF J = 0 THEN
  >> tmp$$$.bas echo   ELSE
  >> tmp$$$.bas echo     MID$(a$, I, 1) = MID$(c$, J, 1)
  >> tmp$$$.bas echo   END IF
  >> tmp$$$.bas echo NEXT I
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "set upcase_=" ; a$
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Show (test) results
  echo upper_=%upcase_%
  ::
  :: Clean up
  for %%f in (tmp$$$.bas tmp###.bat) do if exist %%f del %%f
  set upcase_=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 String
  ::
  :: End
  :_end

The program above can be written in an alternative way where the
QBASIC program is not echoed to the temporary file. The MS-DOS FIND
is used instead to build the auxiliary QBASIC program file. This
version of the trick was brought to my attention by Todd Vargo.
There are a couple of complications. The %1 parameter is not
directly available to the QBASIC program as in the echoing version.
Furthermore, it will be necessary to include the .BAT extension when
calling the batch.
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  echo %1 %2 %3> tmp###.bat
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  set skip=
  find "'Q%skip%B" <%0 > tmp$$$.bas
  goto _jump
  OPEN "tmp###.bat" FOR INPUT AS #1 'QB
  LINE INPUT #1, a$ 'QB
  CLOSE #1 'QB
  LET a$ = LTRIM$(RTRIM$(a$)) 'QB
  LET b$ = "abcdefghijklmnopqrstuvwxyz" 'QB
  LET c$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 'QB
  FOR I = 1 TO LEN(a$) 'QB
    LET J = INSTR(b$, MID$(a$, I, 1)) 'QB
    IF J = 0 THEN 'QB
    ELSE 'QB
      MID$(a$, I, 1) = MID$(c$, J, 1) 'QB
    END IF 'QB
  NEXT I 'QB
  OPEN "tmp###.bat" FOR OUTPUT AS #1 'QB
  PRINT #1, "@echo off" 'QB
  PRINT #1, "set upcase_=" ; a$ 'QB
  CLOSE #1 'QB
  SYSTEM 'QB
  ::
  :: Run the QBASIC program
  :_jump
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Show (test) results
  echo upper_=%upcase_%
  ::
  :: Clean up
  for %%f in (tmp$$$.bas tmp###.bat) do if exist %%f del %%f
  set upcase_=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 String1 [String2] [String3]
  ::
  :: End
  :_end

With ECHON.EXE and UPPER.EXE
  @echo off
  if "%1"=="" goto _usage
  echon set upcase_=>tmp$$$.bat
  echo %1|m:\upper>>tmp$$$.bat
  call tmp$$$.bat
  echo upcase_=%upcase_%
  goto _out
  :_usage
  echo Usage: %0 Whatever
  :_out
  for %%v in (upcase_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

With SED.EXE the solution is
  @echo off
  if not "%1"=="" goto _doit
  echo Usage %0 String
  goto _end
  :_doit
  echo %1|sed "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/"
  :_end
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:09 2002
Subject: Append new directory to path
Date: Fri, 1 Mar 2002 01:00:09
From: ts@uwasa.fi (Timo Salmi)

8. Is there an easy way to append a new directory to the path?
==============================================================

This often needed trick is basically very simple. For example
to add directory %1 to path use
 path=%path%;%1
Note that you can only use this trick in a batch. It will not work
at the MS-DOS prompt because the environment variables are expanded
(%path%) only within batches.

For a full treatment with safeguards against appending non-existing
directories, or appending twice, see ADDPATH.BAT in the collection.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:10 2002
Subject: Comparing two files
Date: Fri, 1 Mar 2002 01:00:10
From: ts@uwasa.fi (Timo Salmi)

9.  How can I compare if two files are identical?
=================================================

It is possible in batch programming to test whether or not two files
have identical contents. This trick utilizes the external MS-DOS
programs fc.exe and find.exe. (An external MS-DOS program means, of
course, a program that comes with the standard MS-DOS releases. Most
often the MS-DOS external support files are located in a c:\dos
directory.)
  @echo off
  fc %1 %2 > tmp$$$
  type tmp$$$ | find /i "fc: no differences encountered" > diffe$$$
  if exist notsame$ del notsame$
  copy diffe$$$ notsame$ > nul
  if not exist notsame$ echo Files %1 and %2 are different
  if exist notsame$ echo Files %1 and %2 are identical
  if exist tmp$$$ del tmp$$$
  if exist notsame$ del notsame$
  if exist diffe$$$ del diffe$$$
If you think about, this idea can be used for other useful purposes,
too, because it establishes whether a given string is found in a
text file.
   The batch in the above is generic across various MS-DOS versions.
For later MS-DOS versions the batch can be simplified since FIND
returns the success of the search as an errorlevel. For more on that
aspect, type "HELP FIND".
  @echo off
  fc %1 %2 | find /i "FC: no differences encountered" > nul
  if errorlevel==2 goto _2
  if errorlevel==1 goto _1
  echo Files %1 and %2 are identical
  goto _continue
  :_1
  echo Files %1 and %2 are different
  goto _continue
  :_2
  echo Error in the FIND search
  :_continue
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:11 2002
Subject: Writing an empty line
Date: Fri, 1 Mar 2002 01:00:11
From: ts@uwasa.fi (Timo Salmi)

10. Writing an empty line to the screen. How is that done?
==========================================================

This is a simple, but an often needed, useful trick. Just use echo
with (for example) a point (.) right after it. No space. As you can
see, I have utilized this batch feature extensively in my batch
collection.
  @echo off
  echo Hello world
  echo.
  echo Batches are fun

 Q: A further question. I wanted to create an empty file but after
  echo.> tmp
the size of the tmp file is two bytes.

 A: Of course it is. The "echo." displays one empty line on the
screen, i.e. inserts an end of line. Thus the contents of the tmp
file will be Hex 0D 0A (ASCII 13, 10 in decimal), just as it should
in MS-DOS. For creating an empty file, please see the relevant,
separate item #42 in this FAQ.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:12 2002
Subject: Customizing the pause message
Date: Fri, 1 Mar 2002 01:00:12
From: ts@uwasa.fi (Timo Salmi)

11. A tip: Customizing the pause message
========================================

You can easily customize the message given by pause by giving your
own with echo and directing the pause message to nul.
to nul.
 echo Break to quit, any other key to remove the tmp directory
 pause > nul
To suppress the linefeed use ECHON.EXE
  echon Break to quit, any other key to remove the tmp directory
  pause > nul
For a more complicated pausing, use the CHOICE command. See items
#44 and #64.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:13 2002
Subject: Complicated renaming with for
Date: Fri, 1 Mar 2002 01:00:13
From: ts@uwasa.fi (Timo Salmi)

12. A tip: Complicated renaming of files with for
=================================================

A considerable number of batch related questions on Usenet news seem
to relate to different kinds of file name handling. One set of these
questions relate to renaming files. Everyone knows the rudiments
like "REN OLDNAME.* NEWNAME.*" but one can conveniently utilize the
FOR statement for more complicated renaming tasks. Although this is
basically trivial, one does not necessarily come to think of it. The
FOR statement is quite useful for involved renaming of files. An
example delineates. For example I have the following files (Turbo
Pascal units for TP 4.0, 5.0, 5.5, 6.0 and 7.0). Say that I wish to
rename them to be version 35 instead of 34.
  tspa3540.zip
  tspa3550.zip
  tspa3555.zip
  tspa3560.zip
  tspa357c.zip
The following for-statement does that conveniently.
 for %f in (40 50 55 60 7c) do ren tspa34%f.zip tspa35%f.zip
Naturally, renaming is not the only task that can utilize this
trick. I am sure you can readily think of others, like
  for %d in (a b) do format %d:

Consider another example. Let's say that we have a number of image
files in a directory, such as e.g.
  1.jpg
  8.jpg
  100.jpg
  2.jpg
  200.jpg
What we wish to do is to rename them so that the names are of equal
length, i.e.
  0001.jpg
  0008.jpg
  0100.jpg
  0002.jpg
  0200.jpg
The following awk solution will prepare a batch file for the
renaming
  @echo off
  ::
  dir /l /b *.jpg>dir$$$.tmp
  ::
  >  tmp$$$.awk echo function lead(s)
  >> tmp$$$.awk echo {p=length(s)
  >> tmp$$$.awk echo plug="0000"
  >> tmp$$$.awk echo return substr(plug,1,4-p)}
  >> tmp$$$.awk echo BEGIN { FS = "." }
  >> tmp$$$.awk echo {
  >> tmp$$$.awk echo printf "ren %%12s %%s%%s.%%s\n",$0,lead($1),$1,$2
  >> tmp$$$.awk echo }
  ::
  type dir$$$.tmp|gawk -f tmp$$$.awk>mybatch.bat
  ::
  for %%f in (tmp$$$.awk dir$$$.tmp) do if exist %%f del %%f
Don't run the built MYBATCH.BAT unless you actually wish to do the
renaming.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:14 2002
Subject: Checking for wildcards
Date: Fri, 1 Mar 2002 01:00:14
From: ts@uwasa.fi (Timo Salmi)

13. How can I check if the file name given included wildcards?
==============================================================

This example shows how you can easily test whether a parameter (%1)
of a batch contains wildcards.
  @echo off
  for %%f in (%1) do if "%%f"=="%1" goto _nowilds
  echo Parameter %1 contains wildcards (or is missing)
  :_nowilds

Another option, with find
  @echo off
  echo %1|find "*">nul
  set wilds_=no
  if errorlevel==0 if not errorlevel==1 set wilds_=yes
  echo %1|find "?">nul
  if errorlevel==0 if not errorlevel==1 set wilds_=yes
  echo %wilds_%
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:15 2002
Subject: Preventing breaking the batch
Date: Fri, 1 Mar 2002 01:00:15
From: ts@uwasa.fi (Timo Salmi)

14. Is it possible to prevent the user from breaking the batch?
===============================================================

Yes, it is possible to prevent the user from interrupting a batch by
using the ctty command to reassign the input (and the output)
device. Here is an example (an elementary password batch requiring
inputting an e). Note the < and > redirections which are needed
while the ctty has been assigned to nul. The ASK batch enhancer is
included in the TSBAT collection. (In the later MS-DOS versions you
could as well use the CHOICE command).
  @echo off
  ctty nul
  echo Now you cannot break the batch with ^C or ^Break > con
  :_ask
  echo Use e to break > con
  ask /b /d < con
  if errorlevel==101 if not errorlevel==102 goto _out
  goto _ask
  :_out
  ctty con
  echo Back to normal. Now you can break the batch with ^C or ^Break.
Note that this trick does not prevent you from rebooting with
alt-crtl-del while the batch is running. For that you need an
external program like noboot.exe from
 107499 Jul 28 1999 ftp://garbo.uwasa.fi/pc/ts/tstsr23.zip
 TSR programs (noboot,reslock,sordino,timedown,timeup ...)
(or whichever version number is current).
The ASK.EXE usage is
 ASK [Prompt] [/b] [/d] [/l] [/t##] [/u]
                :    :    :    :      +- convert to Upper case
                :    :    :    +- Time-out parameter (0 = forever)
                :    :    +- convert to Lower case
                :    +- Direct read (no <-+ needed)
                +- Batch mode (needed only if no prompt)

This question elicits one of the most frequent false answers, i.e.
that the problem is solved by including "BREAK=OFF" in your
CONFIG.SYS file. The BREAK ON/OFF (only) toggles the extended CTRL+C
checking. For more on this see "HELP BREAK".
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:16 2002
Subject: Forcing autoexec.bat execution
Date: Fri, 1 Mar 2002 01:00:16
From: ts@uwasa.fi (Timo Salmi)

15. Can I prevent a break from bypassing the autoexec.bat?
==========================================================

You can actually prevent a quick tapping of the break from bypassing
your autoexec.bat by a variation of the trick in the item above. Put
for example
  shell=c:\command.com /p nul
in your config.sys. Before you do, make sure to have a floppy to
boot from in case something goes wrong. I first saw trick when it
was posted in the Usenet news:comp.os.msdos.programmer newsgroup by
Joseph Gil.

This is not, however, quite all there is to it. You should put
  ctty con
as the last line to your autoexec.bat. If you don't, the keyboard
will not be responding, and you must boot from the floppy you so
sensibly had prepared :-).

The /P switch makes a new permanent copy of the command interpreter.
For the details meaning of the /P switch with C:\COMMAND.COM use
"HELP COMMAND".

As you know pressing F5 before CONFIG.SYS in executed skips
CONFIG.SYS and AUTOEXEC.BAT altogether. On the other hand, it is
possible to disable the bypassing. Set in your GONFIG.SYS "SWITCHES
/N". For more see "HELP SWITCHES".
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:17 2002
Subject: Getting the extension
Date: Fri, 1 Mar 2002 01:00:17
From: ts@uwasa.fi (Timo Salmi)

16. How can I extract the extension of a file name?
===================================================

It would be quite useful to be able to extract the extension of a
given file name into an environment variable. Or to be able just to
test whether there is an extension. Here is how to do that. The
batch is based on the information in PC-Magazine July 1992, Vol. 11,
No. 13, page 528. It gives the crucial information that if one
precedes the argument of a for loop with a slash (/), then the
argument is interpreted in two parts. The first part is the first
character of the argument, the second part all the rest. Neat,
indeed.
   The problem with my solution below is that it will not recognize
.* or .??? as extensions. But, of course, one can first test for
wildcards as shown in a previous item "Checking for wildcards". See
e.g. UNPACK.BAT for the utilization of this method.
  @echo off
  set exten_=%1
  :_next
  set prev_=%exten_%
  for %%f in (/%exten_%) do set exten_=%%f
  if ".%exten_%"=="%prev_%" goto _extfound
  if not "%exten_%"=="%prev_%" goto _next
  goto _noext
  :_extfound
  echo The filename %1 has an extension %exten_%
  goto _out
  :_noext
  echo The filename %1 has no extension
  :_out
  set exten_=
  set prev_=
This solution will not work after MS-DOS version 6.22, i.e. it will
not work under Windows95. A QBASIC alternative is given below
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  >  tmp$$$.bas echo LET a$ = "%1"
  >> tmp$$$.bas echo LET J = INSTR(a$, ".")
  >> tmp$$$.bas echo IF J = 0 THEN
  >> tmp$$$.bas echo   LET e$=""
  >> tmp$$$.bas echo ELSE
  >> tmp$$$.bas echo   LET e$ = MID$(a$, J+1)
  >> tmp$$$.bas echo END IF
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "set exten_=" ; e$
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Show (test) results
  if not "%exten_%"=="" echo The extension in %1 is %exten_%
  if "%exten_%"=="" echo The name %1 has no extension
  ::
  :: Clean up
  for %%f in (tmp$$$.bas tmp###.bat) do del %%f
  set exten_=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 FileName
  ::
  :: End
  :_end

Yet another option is to utilize a unix port like G(nu)AWK. Below is
an awk-based solution
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  ::
  echo %1|gawk -F. '{printf "set exten_=%%s\n",$2}'>tmp$$$.bat
  call tmp$$$.bat
  if not "%exten_%"=="" echo The extension in %1 is %exten_%
  if "%exten_%"=="" echo No extension in %1
  if exist tmp$$$.bat del tmp$$$.bat
  set exten_=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 FileName
  ::
  :: End
  :_end

Yet another option:
  @echo off
  if "%1"=="" goto _usage
  echon set ext_=>tmp$$$.bat
  echo %1|sed "s/^.*[\\]//;s/[^\.]*//;s/\.//">>tmp$$$.bat
  call tmp$$$
  echo The extension of %1 is %ext_%
  goto _end
  ::
  :_usage
  echo Usage: %0 FileName
  ::
  :_end
  for %%v in (ext_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:18 2002
Subject: The quote character %
Date: Fri, 1 Mar 2002 01:00:18
From: ts@uwasa.fi (Timo Salmi)

17. Information on the quote character %
========================================

As we know %1 indicates the first parameter given to a batch. Thus
for example echo %1 echoes that parameter. But what if you want to
echo the actual string %1 instead. The % character acts as a quote
character. Thus echo %%1 will indeed be a "%1" instead of its usual
interpretation. Try the following simple test
  @echo off
  if "%1"=="" goto _out
  echo %1
  echo %%1
  :_out
See the item on "Eliminating auxiliary batches" for utilizing this
feature. A good example of utilizing this feature is given by
DELPATH.BAT. If you prepare a new batch file using a batch file
you'll need to use the % character double. A demonstration:
  @echo off
  echo>  test$$$.bat @echo off
  echo>> test$$$.bat if not "%%1"=="" echo %%1
  call test$$$ Success
  if exist test$$$.bat del test$$$.bat
Had you used only "%1", no "Success".
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:19 2002
Subject: Eliminating auxiliary batches
Date: Fri, 1 Mar 2002 01:00:19
From: ts@uwasa.fi (Timo Salmi)

18. Eliminating auxiliary batches (making do with the main batch)
=================================================================

Quite a number of batch programming tasks require an auxiliary batch
which the primary batch has to call. Many of these cases can be
eliminated by making the batch call itself (a kind of recursion).
The auxiliary code is put in the batch itself. The trick is best
illustrated by looking at the SHOW.BAT, which provides a wild-carded
TYPE command, and would normally need an auxiliary file to type each
of the individual files. Another example is given by the SAFEDEL.BAT
batch.
   There is also an another trick for a similar purpose. The primary
batch creates and auxiliary batch or batches, which it then calls.
See DELPATH.BAT for an example of this method. Here is also a simple
demonstration listing the drives on your system. (Only from c to t,
actually because of the wrap I use here).
 @echo off
 echo @echo off> tmp$$$.bat
 echo if exist %%1:\nul echo Drive %%1: is present>> tmp$$$.bat
 for %%d in (c d e f g h i j k l m n o p q r s t) do call tmp$$$ %%d
 del tmp$$$.bat
   There was an inventive twist of this method in PC-Magazine August
1992, Vol. 11, No. 14, p. 527 for getting the volume label of a
disk. Here is my own example using the same techniques. It sets the
current directory in an environment variable getdir_. I have
utilized this technique in PUSHDIRE.BAT.
  @echo off
  echo @echo off> director.bat
  echo set getdir_=%%2>> director.bat
  echo echo %%getdir_%%>> director.bat
  dir | find "Directory"> go.bat
  call go
  if exist director.bat del director.bat
  if exist go.bat del go.bat
Incidentally, this is one example of a batch that will not work in
the Windows 95 DOS box. Since the long file names are supported you
have to use "directory.bat" instead of "director.bat" in the above!
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:20 2002
Subject: Subst utilization in paths
Date: Fri, 1 Mar 2002 01:00:20
From: ts@uwasa.fi (Timo Salmi)

19. Utilizing the subst command in paths
========================================

I use the following kind of a simple batch to make some of my
directories easy to reach. The way this simple batch is written it
avoids unnecessary errors if the substitution already has been made.
As a last measure it shows the current substitution status.
  @echo off
  if exist m:\nul echo The substitution has already been made
  if not exist m:\nul subst m: c:\math
  if not exist s:\nul subst s: c:\support
  subst
A warning. This advice is for vanilla MS-DOS only. If you have
Windows, it is highly inadvisable to use the SUBST command.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:21 2002
Subject: Run a batch one a week
Date: Fri, 1 Mar 2002 01:00:21
From: ts@uwasa.fi (Timo Salmi)

20. How can I run a batch once a week (testing for the weekday)
===============================================================

The crucial trick is to be able to put the weekday into an
environment variable. For the full treatment see WEEKLY.BAT. The
essential trick needed is below, that is capturing the weekday into
a weekday_ environment variable. No auxiliary programs outside the
normal MS-DOS commands are needed.
  @echo off
  echo.| date | find "Current" > tmp$$$.bat
  echo set weekday_=%%3> current.bat
  call tmp$$$
  echo %weekday_%
  if "%weekday_%"=="Fri" echo Thank God it's Friday
  if exist tmp$$$.bat del tmp$$$.bat
  if exist current.bat del current.bat
  set weekday_=
In fact, if you substitute %%4 for the %%3 in the above, you'll
capture today's date. Neat, eh?

There is an endless number of variations of solving the same
problem. Here is another rendition this this one:
  @echo off
  echo @prompt set date_=$d> tmp$$$.bat
  %comspec% /c tmp$$$> tmp2$$$.bat
  call tmp2$$$
  echo %date_% > tmp$$$.bat
  type tmp$$$.bat | find "Fri" > nul
  if errorlevel==0 if not errorlevel==1 echo It's Friday
  if errorlevel==1 if not errorlevel==2 echo It's not Friday
  set date_=
  for %%f in (tmp$$$.bat tmp2$$$.bat) do del %%f

Quite a clever rendition of the methods discussed on the Usenet news
to get the weekday was posted by William Allen based on Benny
Pedersen's echo.exit idea. A 6.22 version with my own habits of
formulation is given below. The first one below also is a useful
demonstration of the usage of the choice pipe.
  @echo off
  echo.exit|%comspec% /e:2048/k prompt $d$_rem |choice /s/n/c:MeWhFan>nul
  if errorlevel==7 if not errorlevel==8 set wd_=Sun
  if errorlevel==6 if not errorlevel==7 set wd_=Sat
  if errorlevel==5 if not errorlevel==6 set wd_=Fri
  if errorlevel==4 if not errorlevel==5 set wd_=Thu
  if errorlevel==3 if not errorlevel==4 set wd_=Wed
  if errorlevel==2 if not errorlevel==3 set wd_=Tue
  if errorlevel==1 if not errorlevel==2 set wd_=Mon
  if errorlevel==0 if not errorlevel==1 set wd_=Error
  echo %wd_%
  set wd_=
Or, if one wants to play around with the order of the errorlevels to
condense a bit, then
  @echo off
  echo.exit|%comspec% /e:2048/k prompt $d$_rem |choice /s/n/c:MeWhFan>nul
  if errorlevel==0 set wd_=Error
  if errorlevel==1 set wd_=Mon
  if errorlevel==2 set wd_=Tue
  if errorlevel==3 set wd_=Wed
  if errorlevel==4 set wd_=Thu
  if errorlevel==5 set wd_=Fri
  if errorlevel==6 set wd_=Sat
  if errorlevel==7 set wd_=Sun
  echo %wd_%
  set wd_=
One method, but it only _displays_ the weekday, is
  @echo off
  echo.exit|%comspec% /e:2048/k prompt @set wd_=$d$h$h$h$h$h$h$h$h$h$h$h$_rem >tmp$$$.bat
  call tmp$$$.bat
  echo %wd_%
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  set wd_=
The problem with it is that the backspaces will go embedded into the
variable! Thus it is of little practical use. With (g)awk it is
fairly simple to extract the proper format
  echo.exit|%comspec% /e:2048/k prompt @set wd_=$d$_rem >tmp$$$.bat
  call tmp$$$.bat
  echo %wd_%|gawk '{printf "@set wd_=%%s\n",$1}'>tmp$$$.bat
  call tmp$$$.bat
  echo %wd_%
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  set wd_=

Maybe the easiest solution is using the echon.exe (echo without the
trailing linefeed) and cut.exe column cutter programs.
  @echo off
  ::
  rem Store the date into a file
  echo.|date|find "Current ">tmp$$$.txt
  ::
  rem Build an auxiliary batch to set the weekday variable
  echon @set wd_=>tmp$$$.bat
  type tmp$$$.txt|cut 17 19>>tmp$$$.bat
  ::
  rem Call the batch that has been built
  call tmp$$$.bat
  ::
  rem Demonstrate the result
  echo wd_=%wd_%
  ::
  :: Clean up
  set wd_=
  for %%f in (tmp$$$.txt tmp$$$.bat) do if exist %%f del %%f

In the above the
  type tmp$$$.txt|cut 17 19>>tmp$$$.bat
could be replaced by
  type tmp$$$.txt|gawk '{printf "%%s\n",substr($0,17,3)}'>>tmp$$$.bat

Or
  echon @set wd_=>tmp$$$.bat
  type tmp$$$.txt|cut 17 19>>tmp$$$.bat
could be replaced by
  type tmp$$$.txt|cut 17 19|sed "s/.*/@set wd_=&/">tmp$$$.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:22 2002
Subject: Is path included in file name?
Date: Fri, 1 Mar 2002 01:00:22
From: ts@uwasa.fi (Timo Salmi)

21. How can I test if a given file name includes the path?
==========================================================

First of all please see the earlier item "How can I extract the
extension of a file name?" because the same ideas are drawn upon.
Testing whether the file name is a bare file name like go.exe or
includes a path like r:\progs\go.exe is quite a complicated task if
one wants to allow wildcarded names like r:\progs\*.exe. This can be
done, and here is how. If one can figure this one out, one can
safely say that one has begun to understand batch files.
  @echo off
  echo @echo off> tmp$$$.bat
  echo set rest_=%%1>> tmp$$$.bat
  echo :_next>> tmp$$$.bat
  echo set prev_=%%rest_%%>> tmp$$$.bat
  echo for %%%%g in (/%%rest_%%) do set rest_=%%%%g>> tmp$$$.bat
  echo if ":%%rest_%%"=="%%prev_%%" goto _found>> tmp$$$.bat
  echo if "\%%rest_%%"=="%%prev_%%" goto _found>> tmp$$$.bat
  echo if not "%%rest_%%"=="%%prev_%%" goto _next>> tmp$$$.bat
  echo goto _nopath>> tmp$$$.bat
  echo :_found>> tmp$$$.bat
  echo set haspath_=yes>> tmp$$$.bat
  echo goto _out>> tmp$$$.bat
  echo :_nopath>> tmp$$$.bat
  echo set haspath_=no>> tmp$$$.bat
  echo :_out>> tmp$$$.bat
  echo set rest_=>> tmp$$$.bat
  echo set prev_=>> tmp$$$.bat
  for %%f in (%1) do call tmp$$$ %%f
  if "%haspath_%"=="yes" echo Filename %1 includes a path
  if "%haspath_%"=="no" echo Filename %1 does not include a path
  rem if exist tmp$$$.bat del tmp$$$.bat
  set haspath_=
Note: This will not work for MS-DOS 7 (i.e. the Windows95 version).
The above batch is based on an pre-7 MS-DOS quirk that a line like
  for %%f in (/name) do echo %%f
will return
  n
  ame

Anoter solution, using ECHON.EXE and SED.EXE
  @echo off
  if "%1"=="" goto _usage
  echon set flenme_=>tmp$$$.bat
  echo %1|sed "s/^.*[\\]//">>tmp$$$.bat
  call tmp$$$
  if "%1"=="%flenme_%" echo No path in %1
  if not "%1"=="%flenme_%" echo Path in %1
  goto _end
  :_usage
  echo Usage %0 FileName
  :_end
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  for %%v in (flenme_) do set %%v=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:23 2002
Subject: Display the time
Date: Fri, 1 Mar 2002 01:00:23
From: ts@uwasa.fi (Timo Salmi)

22. How can I display the time without having to press enter?
=============================================================

A simple trick to show the current time:
  echo.| time | find /v "new"
For capturing the time into an environment variable see
LASTBOOT.BAT and item #34.
   The /V switch means displaying all lines not containing the
specified string. This gets rid of the "Enter new time:" line.
   A more complicate way is first getting the time into an
environment variable. A trick utilized also elsewhere in this batch
FAQ.
  @echo off
  echo @prompt set time_=$t> tmp$$$.bat
  %comspec% /c tmp$$$> tmp2$$$.bat
  call tmp2$$$
  echo %time_%
  del tmp$$$.bat
  del tmp2$$$.bat
Or, with G(nu)AWK:
  echo.|time|gawk 'NR==1 {printf "%%s\n",$0}'
Or, with SED.EXE
  echo.|time|sed -n 1p
or just the time, without the (tens of) milliseconds
  echo.|time|sed -n 1p;|sed "s/^.*  //;s/ //g;s/,.*//"

If you want both the date and the time on the same line apply e.g.
  echo.|date|find "Current"|gawk '{printf "%%s",$5}'
  echo.|time|find "Current"|gawk '{printf " %%s\n",$4}'
This option is particularly useful for making logs of events.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:24 2002
Subject: Testing for the errorlevel
Date: Fri, 1 Mar 2002 01:00:24
From: ts@uwasa.fi (Timo Salmi)

23. Alternatives for testing for the errorlevel value
=====================================================

Many programs and some MS-DOS commands (like diskcomp, format and
xcopy) return an errorlevel exit code on termination. Testing for
the errorlevel is complicated by the cumulative nature of
errorlevels. Thus if you wish to test if the errorlevel was
(exactly) 2, you must use
 if errorlevel==2 if not errorlevel==3 echo Errorlevel 2
Another alternative is utilizing the for command:
 for %%e in (0 1 2 3 4 5 6 7) do if errorlevel==%%e set _errlev=%%e
 if "%_errlev%"=="2" echo Errorlevel 2
Alternatively, and more generally
 for %%e in (0 1 2 3 4 5 6 7) do if errorlevel==%%e set _errlev=%%e
 if "%_errlev%"=="2" echo Errorlevel %_errlev%
A convenient trick in more complicated batches is using the goto
command:
  for %%e in (0 1 2) do if errorlevel==%%e goto _label%%e
  goto _out
  :_label0
  echo Errorlevel 0
  :_label1
  echo Errorlevel 1
  :_label2
  echo Errorlevel 2
  :_out
See BOOT.BAT for actual usage of this technique.
   The order in which there errorlevels are given is significant,
since all the errorlevels up to the one returned will be on.
Consider the following demonstration
  @echo off
  find "hello" con > nul
  if errorlevel==2 echo errorlevel 2  % error in search %
  if errorlevel==1 echo errorlevel 1  % no match found %
  if errorlevel==0 echo errorlevel 0  % match found %
If you type
  peekaboo
  ^Z
you'll get *both*
  errorlevel 1
  errorlevel 0
If you type
  hello
  ^Z
then you'll get just
  errorlevel 0

If you are ready allow a few additional lines for a more clear
documentation and easier to understand logic, consider this example
of choice usage
  @echo off
  choice /tq,5 /cynq
  for %%i in (0 1 2 3 255) do if errorlevel==%%i set i_=%%i
  if "%i_%"=="0" echo You effected a break
  if "%i_%"=="1" echo Your input was Yes
  if "%i_%"=="2" echo Your input was No
  if "%i_%"=="3" echo Your input was Quit
  if "%i_%"=="255" echo Error condition
  set i_=

A more complicated example
  @echo off
  if "%2"=="recurse" goto _for
  choice /s/c:123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  for %%j in (0 1 2 3 4 5 6) do call %0 %%j recurse
  goto _level
  :_for
  for %%i in (%10 %11 %12 %13 %14 %15 %16 %17 %18 %19 255) do if errorlevel==%%i set i_=%%i
  goto _exit
  ::
  :_level
  echo errorlevel==%i_%
  :_exit

Speaking of errorlevels, some novice batch users occasionally ask
for the general list of the meanings of the errorlevel values. This
question is based on a failure to grasp how errorlevels are created.
Batch files can only test their values. It is the executable
programs that can return errorlevel values. What the particular
values are, which an executable program returns, is totally
dependent on the individual program. Some programs may be able to
return a lot of different values, some none (or rather return zero
errorlevel). Perhaps this misunderstanding is based on the fact that
MS-DOS includes a number of programs, such as CHOICE.COM, FIND.EXE
and XCOPY.EXE which return errorlevels. For example MS-DOS version
6.22 XCOPY.EXE can signal with six different errorlevel values (0 to
5) depending on the outcome. For those values see "HELP XCOPY" and
select "Notes".

As explained, the errorlevel returned depends on the program called.
But if you wish to set a specific errorlevel within a batch file
(e.g. for testing purposes), you can use G(nu)AWK exit as follows
within your batch file
  echo.|gawk '{exit N}'
where you can put as N anything between 0 and 255.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:25 2002
Subject: Redirecting batch output
Date: Fri, 1 Mar 2002 01:00:25
From: ts@uwasa.fi (Timo Salmi)

24. About redirecting a batch file's output
===========================================

Output from within a batch file is easily redirected. Consider a
batch file example.bat with the following contents
  @echo This is a redirection test> test
Running "example" will produce a file "test" with
  This is a redirection test
The line has an eoln (end of line: ascii 13 + 10) at the end.
Note that it often is advisable not to leave any blank in front of
the > redirection operator.

Redirecting the output that a batch produces, is more complicated.
Consider a batch file example2.bat with the following contents
  @echo This is another redirection test
Running
  example2 > test
will produce an empty "test" file, while the text is echoed on the
standard output. To redirect the output, you need to drive the batch
through the command interpreter command.com like this (provided that
command.com is at path or in the current directory).
  command /c example2 > test
This will redirect the text to the "test" file.

There is another quirk of redirection in MS-DOS batch programming
best demonstrated by an example:
  @echo off
  rem This line will create an empty tmp.$$ file > tmp.$$
  :: This line will not create an empty tmp.$$$ file > tmp.$$$
  rem This line will cause problems: Press <ESC>
  :: This line will not cause problems: Press <ESC>
As explained in PC Magazine Vol 12, Number 9, November 9, 1993, the
reason is that the :: is taken as a label and not processed while
the rem basically is an MS-DOS command that will be processed. The
processing will start from the redirection at the end. This is the
the also reason why redirection and the MS-DOS FOR command will
cause problems. (See the entry "For loop and redirection quirks".)

 A2: Tom Lavedas posted this informative tip on the Usenet news:
"Redirection only works for programs. A batch file is not a program,
but rather a script for the command processor. The command processor
IS a program, so the solution [to redirect output from a batch] is
to run the batch under a secondary copy of the command processor,
something like this ...
  %comspec% /e:2048 /c batch.bat > xy
The version 6.xx's on-line HELP command has some discussion of
redirection and additional details about using COMMAND.COM."
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:26 2002
Subject: Out of environment space
Date: Fri, 1 Mar 2002 01:00:26
From: ts@uwasa.fi (Timo Salmi)

25. How can I test if my batch has sufficient environment space?
================================================================

If your batch utilizes environment variables there is a possibility
that you run out of environment space. If you get an "Out of
environment space" message the well-known trick to increase your
environment space by using shell configuration in config.sys:
  Example: shell=c:\bin\command.com c:\bin /e:1024 /p

A perhaps less-known trick is that you can test in advance if your
batch will run out of environment space. Below is an example showing
you how to test if you have an additional 32 bytes of environment
space still available for your batch:
  @echo off
  set test_=12345678901234567890123456789012
  if "%test_%"=="12345678901234567890123456789012" goto _yes
  echo Insufficient environment space
  goto _out
  :_yes
  echo Sufficient environment space
  set test_=
  rem Whatever you wish to do
  :_out
To test more extensively you can use
  @echo off
  for %%f in (a b c d e f g h i j k l m n o p q r s t u v x y z)
    do set %%f=12345678901234567890123456789012345678901234567890
  set
  echo "%z%"
  if "%z%"=="12345678901234567890123456789012345678901234567890" goto _yes
  echo Insufficient environment space
  goto _out
  :_yes
  echo Sufficient environment space
  :_out
Note! The "for" and "do" should  go on the same line but have been
wrapped for legibility because of the right margin.
   If you want to know what your initial environment size is, you
can use a program like SYSINFO.EXE from
 148195 May 17 1998 ftp://garbo.uwasa.fi/pc/ts/tsutil44.zip
 Timo's 1st utility set (sysinfo,dirw,dtetimal,timelog,...)
or whatever is the current version number of the package.

If you have a batch where getting it right is critical, it is
advisable to test for the environment space sufficiency. The sample
batch below describes the principle
  @echo off
  set critical_=whatever
  if not "%critical_%"=="whatever" goto _nospace
  echo Rest of the batch, no problem
  goto _out
  ::
  :_nospace
  echo Warning: Out of environment space, batch terminated
  ::
  :_out
  set critical_=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:27 2002
Subject: Disabling a drive
Date: Fri, 1 Mar 2002 01:00:27
From: ts@uwasa.fi (Timo Salmi)

26. Is there a simple trick to "disable" or hide a drive?
=========================================================

It you wish temporarily disable a drive use the subst command for
example as follows
  @echo off
  md c:\none
  subst d: c:\none
To enable it again use
  @echo off
  subst d: /d
  rd c:\none
A warning. This advice is for vanilla MS-DOS only. If you have
Windows it is highly inadvisable to use the SUBST command.
   In ftp://garbo.uwasa.fi/pc/ts/ts5dos11.zip there is program
DRIVEOFF.EXE which can be used to disable/enable a drive. It
requires at least MS-DOS version 5.0.
   Directories are much easier. You can hide a directory with the
standard ATTRIB +H command, just like any ordinary file. E.g. I have
moved some of the stronger commands, such as FDISK.EXE, FORMAT.COM
and DELTREE.EXE, to a hidden directory away from C:\DOS to diminish
the risk of calling them inadvertently.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:28 2002
Subject: Escape sequence to printer
Date: Fri, 1 Mar 2002 01:00:28
From: ts@uwasa.fi (Timo Salmi)

27. How can I send an escape sequence to the printer?
=====================================================

Here is a truly trivial trick. You cannot send escape sequences to
the printer directory from the command line, but it is quite easy to
do that from a simple batch file:
  @echo ESC%1> prn
where you have to replace the ESC by the true escape character using
your preferred editor. One snag with this methods is that it imposes
a linefeed.
   In this connection it is often also asked how one does enter the
escape character in a text editor. This, of course, depends on the
editor you are using. Nevertheless, with most editors you can either
enter 27 from the numerical part of the keyboard while holding down
the alt key (ALT-27). Furthermore, the combination of CTRL-P ESC is
a rather common convention in many text editors. If you are using
Windows 3.1x, one additional option is to use Character Map, located
in Accessories, to first copy the escape character to the clipboard.
   A similar, easier task is to eject a page from the printer. This
simply needs
  @echo off
  echo ALT-12>prn
where the ALT-12 stands for the form feed (page break) character
ASCII 12.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:29 2002
Subject: Creating a random string
Date: Fri, 1 Mar 2002 01:00:29
From: ts@uwasa.fi (Timo Salmi)

28. Is it possible to create a random string in a batch?
========================================================

I was asked on the UseNet news how to create a random string. My
reply. Please study the following example and expand on it
  @echo off
  echo 10 randomize(val(mid$(time$,7,2))) > tmp.bas
  echo 20 open "tmp2.bat" for output as #1 >> tmp.bas
  echo 30 x$ = mid$(str$(int(rnd*10000)),2) >> tmp.bas
  echo 40 print #1,"@set random_=";x$ >> tmp.bas
  echo 50 close #2 >> tmp.bas
  echo 60 system >> tmp.bas
  gwbasic tmp.bas
  call tmp2
  for %%f in (tmp.bas tmp2.bat) do if exist %%f del %%f
  set
Alternatively, depending on your MS-DOS version, you might have not
have GWBASIC but QBASIC. In that case use replace the eighth line
with "qbasic /run tmp.bas".

With the external FN.EXE program creating a random number it is
easy:
  @echo off
  fn /e rnd(10000) 0>nul
  echo A random number between 0 and 9999: %fn_%
  set fn_=

Another trick is to use the (tens of) milliseconds from the time
(needs CUT.EXE)
  @echo off
  echo.exit|%comspec% /e:2048/k prompt set rnd_=$t|cut 1 9 19 20>tmp$$$.bat
  call tmp$$$
  echo A two digit pseudo-random number: %rnd_%
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  set rnd_=

Using G(nu)AWK the solution is
  @echo off
  echo.|gawk '{srand();printf "set rand_=%%s\n",int(10000*rand())}'>tmp$$$.bat
  call tmp$$$.bat
  echo rand_=%rand_%
  for %%v in (rand_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:30 2002
Subject: Getting string length
Date: Fri, 1 Mar 2002 01:00:30
From: ts@uwasa.fi (Timo Salmi)

29. Is it possible in a batch to find out the length of a string?
=================================================================

The task of finding out the length of a string was tackled in PC
Magazine January 26, 1993 issue. The solution is my own and more
general, but naturally it has similar ingredients to the PC
Magazine's.
  @echo off
  set test_=Testing the length of a string
  echo %test_%> len$&$&$
  dir len$&$&$ | find "LEN$&$&$" > go$$$.bat
  echo @echo off> len$&$&$.bat
  echo set length_=%%1>> len$&$&$.bat
  call go$$$
  echo The length is %length_% bytes
  for %%f in (len$&$&$ len$&$&$.bat go$$$.bat) do del %%f
The problem with this solution is that it off by +2 bytes. A clever
vanilla batch-only solution to counter this problem was posted by
John Savage. It is based on making an auxiliary file with suitable
size and doing away with the first digit. The names of the files
have been adjusted
  @echo off
  set test_=Testing the length of a string
  echo:..%test_%>tmp$$$.txt
  for %%i in (1 2 3 4 5 6 7 8) do echo 1234567890>>tmp$$$.txt
  dir tmp$$$|find "TMP$$$   TXT">tmp$$$2.bat
  echo set lenght_=%%2>tmp$$$.bat
  call tmp$$$2
  echo set length_=%%%lenght_%>tmp$$$.bat
  call tmp$$$
  for %%f in (tmp$$$*.*) do if exist %%f del %%f
  echo %test_%
  echo length_=%length_%
  set length_=
If you wish to understand the internal logic, add typing the files
made, to see what is in them.

Another solution version, now by yours truly, using SED.EXE,
ECHON.EXE and FN.EXE
  @echo off
  set test_=Testing the length of a string
  ::
  echo %test_%>tmp$$$.txt
  echon @set size_=>tmp$$$.bat
  dir tmp$$$.txt|sed -n 5p|cut 20 26|sed -e "s/\.//g" -e "s/ //g">>tmp$$$.bat
  call tmp$$$
  fn /e %size_%-2 0>nul
  ::
  echo %test_%
  echo The length is %fn_% bytes
  ::
  for %%v in (test_ size_ fn_) do set %%v=
  for %%f in (tmp$$$.txt tmp$$$.bat) do if exist %%f del %%f
Note that the  sed -e "s/\.//g" -e "s/ //g"  could also be written
as  sed "s/\.//g;s/ //g"

A G(nu)AWK-based auxiliary program solution would be
  @echo off
  set test_=Testing the length of a string
  echo %test_%|gawk '{printf"set length_=%%s\n",length()}'>len$&$&$.bat
  call len$&$&$.bat
  echo The length is %length_% bytes
  set length_=
  if exist len$&$&$.bat del len$&$&$.bat

Also the choice command can be used for the task. However, this
gives the length+1.
  @echo off
  echo |choice /t,0 /c%1>nul
  for %%i in ( 0 1 2 3 4 5 6 7 8 9) do if errorlevel==%%i set i_=%%i
  for %%i in ( 10 11 12 13 14 15 16 17 18 19) do if errorlevel==%%i set i_=%%i
  for %%i in ( 20 21 22 23 24 25 26 27 28 29) do if errorlevel==%%i set i_=%%i
  for %%i in ( 30 31 32 33 34 35 36 37 38 39) do if errorlevel==%%i set i_=%%i
  for %%i in ( 40 41 42 43 44 45 46 47 48 49) do if errorlevel==%%i set i_=%%i
  echo The length+1 of %1 is %i_%
  set i_=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:31 2002
Subject: MS-DOS version into environment variable
Date: Fri, 1 Mar 2002 01:00:31
From: ts@uwasa.fi (Timo Salmi)

30. How to obtain the MS-DOS version into an environment variable?
==================================================================

Here is the code how to do it.
  @echo off
  ver > go$$$.bat
  echo @echo off> ms-dos.bat
  echo set version_=%%2>> ms-dos.bat
  call go$$$
  echo Your MS-DOS version is %version_%
  del go$$$.bat
  del ms-dos.bat

Or, with G(nu)AWK (untested beyond 6.22)
  @echo off
  set ver_=Pre6
  echo Test|find "Fail"
  if errorlevel==1 set ver_=Win
  ver|find "Windows"
  if errorlevel==1 goto _msdos
  goto _output
  :_msdos
  ver|find "Version"|gawk '{printf "set ver_=%%s\n",$3}'>tmp$$$.bat
  call tmp$$$
  :_output
  echo ver_=%ver_%
  ::
  rem Clean up
  set ver_=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

MS-DOS 5.0 version introduced many enhancements (like the loadhigh
command, etc) and additions to the command switches (like /B and /S
to the DIR command). Therefore it is useful to be able to test
whether the batch is being run on a system that is at least MS-DOS
5.0. Below is one option for vanilla MS-DOS versions. Version 7 is
not included, because it is a non-independent Windows95 addition.
  rem Establish whether MS-DOS version 5.0 or later is being used
  set isver50_=
  ver | find "5.0" > tmpfind.$$$
  ver | find "6.0" >> tmpfind.$$$
  ver | find "6.2" >> tmpfind.$$$
  copy tmpfind.$$$ tmpfind1.$$$ > nul
  del tmpfind.$$$
  if exist tmpfind1.$$$ set isver50_=yes
  if exist tmpfind1.$$$ del tmpfind1.$$$

For some more see the following postings with Message-ID:
 <a3g1d9$pal@poiju.uwasa.fi>
 <a3pfe3$c13$10@news1.xs4all.nl>
using http://groups.google.com/advanced_group_search
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:32 2002
Subject: Number of files on a drive
Date: Fri, 1 Mar 2002 01:00:32
From: ts@uwasa.fi (Timo Salmi)

31. How can I find out the number of regular files on a drive?
==============================================================

Try
  @echo off
  attrib /s c:\*.* | find /c "\"
The directories will not be (mis)counted as files as would with the
dir command. Besides the dir command is not recursive until MS-DOS
version 5.0.
   Note that if you do this for the same drive where your %TEMP%
variable points, or if you have not set that variable, if you do it
on the default drive, you will get two too many in the count because
of the "|" pipe.
   The /S switch in ATTRIB makes the command traverse also
subdirectories. The /C switch in FIND gives the number of the
matches found.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:33 2002
Subject: Numbering a file's lines
Date: Fri, 1 Mar 2002 01:00:33
From: ts@uwasa.fi (Timo Salmi)

32. How can I use a batch to augment line numbers to my text file?
==================================================================

Occasionally it might be useful to put line number to a text file.
Here is an example how to do it with MS-DOS commands only
  @echo off
  type YourFile.txt | find /v /n "&$&$&$123" > YourNew.txt
The parameter &$&$&$123 stands for an improbable string, since find
/v means displaying all the lines not containing it. The switch /n
means numbering the lines. Or use
  type YourFile.txt|find /v/n "">YourNew.txt

A G(nu)AWK alternative to insert line numbers would be
  type YourFile.txt|gawk '{printf "[%%s]%%s\n", NR, $0}'>YourNew.txt
or, if you wish to display the line number above each line
  @echo off
  ::
  if %temp%=="" echo The TEMP variable not set (in AUTOEXEC.BAT)
  if %temp%=="" goto _out
  ::
  echo {printf "line=%%s\n", NR} > %temp%\tmp.awk
  echo {printf "%%s\n", $0} >> %temp%\tmp.awk
  type YourFile.txt|gawk -f %temp%\tmp.awk>YourNew.txt
  if exist %temp%\tmp.awk del %temp%\tmp.awk
  ::
  :_out
This example also demonstrates the usage of the TEMP variable
usually set in the AUTOEXEC.BAT. Furthermore, it demonstrates taking
the GAWK commands from a file.

What if you wish to put leading zeroes into the line numbers so that
the new column thus formed stays the same width throughout. Here is
how to do that with GAWK.
  @echo off
  ::
  >  tmp$$$.awk echo {p=length(NR)
  >> tmp$$$.awk echo lead="000"
  >> tmp$$$.awk echo lead=substr(lead,1,4-p)
  >> tmp$$$.awk echo printf"%%s%%s %%s\n",lead,NR,$0}
  ::
  type myfile.txt|gawk -f tmp$$$.awk
  for %%f in (tmp$$$.awk) do if exist %%f del %%f

If you just wish to have the columns straight, use e.g.
  type myfile.txt|gawk '{printf "%%4.0f %%s\n",NR,$0}'

Here is an interesting, but probably a fairly useless little twist:
  @echo off
  dir /b | find /v /n "&$&$&$123" | find "[20]"
It tells you which is the twentieth entry in a directory.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:34 2002
Subject: Push and pop a directory
Date: Fri, 1 Mar 2002 01:00:34
From: ts@uwasa.fi (Timo Salmi)

33. Storing and returning to the original directory (push and pop)
==================================================================

There are several methods for (non-resident) pushing and popping the
directory by batch file techniques. In other words storing the
current directory, changing the directory in between, and then
returning to the starting directory. PUSHDIRE.BAT and POPDIRE.BAT
give one method where the current drive and directory are stored in
environment variables. The second method, displayed below, is a
direct adaptation from Jeff Prosise's column in PC Computing, March
1993, pp. 216-217. Later the trick was presented again in PC
Magazine June 14, 1994, Vol. 13, No. 11, p. 357. The method is a
very clever utilization of the prompt system. An example
illustrates.
  @echo off
  echo @prompt cd $p$_$n:> c:\setback.bat
  %comspec% /c c:\setback> c:\goback.bat
  ::
  rem Change the drive and directory
  c:
  cd \dos
  echo The current directory is
  cd
  rem Do whatever you wish to do there
  pause
  ::
  rem Go back to the original drive and directory
  call c:\goback
  echo Now back in the original directory
  cd
  ::
  rem cleanup
  if exist c:\setback.bat del c:\setback.bat
  if exist c:\goback.bat del c:\goback.bat

 A2: Tom Lavedas posted in on the Usenet news "I think the best way
to get the current directory's name into the environment is to use
something like this ...
  @echo off
   echo @prompt set CurrentLoc=$p$_ > {tempA}.bat
   %comspec% /e:2048 /c {tempA}.bat > {tempB}.bat
   for %%v in ({tempB}.bat del) do call %%v {temp?}.bat
This approach extracts the information from the PROMPT command's
output, making use of that statement's dollar sign equivalences."

Another option for putting the current drive and directory
information into environment variables is as follows. You can easily
take it up from there. Attributions are in the next item #34.
  echo.exit|%comspec% /k prompt @set var1_=$n$_rem >m:\tmp$$$.bat
  call m:\tmp$$$.bat
  echo.exit|%comspec% /k prompt @set var2_=$p$_rem >m:\tmp$$$.bat
  call m:\tmp$$$.bat
  echo Current drive:     %var1_%
  echo Current directory: %var2_%
  for %%f in (m:\tmp$$$.bat) do if exist %%f del %%f
  set var1_=
  set var2_=

Using the auxiliary programs echon and cut, the setting of the
environment variables needed are demonstrated by
  @echo off
  ::
  :: Get the information
  echon @set drive_=>tmp$$$.bat
  cd|cut 1 1>>tmp$$$.bat
  echon @set curdir_=>>tmp$$$.bat
  cd>>tmp$$$.bat
  ::
  :: Call the batch that has been built
  call tmp$$$.bat
  ::
  :: Show the results
  echo drive_=%drive_%
  echo curdir_=%curdir_%
  ::
  :: Clean up
  for %%v in (drive_ curdir_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
In the above it is possible to utilize SED.EXE instead of ECHON.EXE
and CUT.EXE by replacing
  ::
  :: Get the information
  echon @set drive_=>tmp$$$.bat
  cd|cut 1 1>>tmp$$$.bat
  echon @set curdir_=>>tmp$$$.bat
  cd>>tmp$$$.bat
with
  ::
  :: Get the information
  cd|sed -e "s/:.*//" -e "s/.*/@set drive_=&/">tmp$$$.bat
  cd|sed "s/.*/@set curdir_=&/">>tmp$$$.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:35 2002
Subject: Date into environment variable
Date: Fri, 1 Mar 2002 01:00:35
From: ts@uwasa.fi (Timo Salmi)

34. Enticing the current date into an environment variable
==========================================================

Like in the item "Storing and returning to the original directory"
there are more than one way of doing this. One method is indicated
in the item "How to run a batch once a week". The other (again)
utilizes the prompt:
  @echo off
  echo @prompt set date_=$d> c:\tmp$$$.bat
  %comspec% /c c:\tmp$$$> c:\tmp2$$$.bat
  call c:\tmp2$$$
  echo %date_%
  del c:\tmp$$$.bat
  del c:\tmp2$$$.bat
If you look at your MS-DOS manual for the prompt special $ codes
(like $d) that you can use in the prompt, you'll see that this
method opens quite a number of possibilities of putting information
into environment variables. Exercise: Put the current weekday into
an environment variable. Hint: Apply $d and $h.

There is a clever variation in using the new instance of the MS-DOS
command interpreter drawn from postings of William Allen on "the
ExitPrompt method devised by Benny Pedersen". It is otherwise quite
similar to the above, but needs creating only one auxiliary batch
file.
  @echo off
  echo.exit|%comspec% /k prompt @set date_=$d$_rem >tmp$$$.bat
  call tmp$$$.bat
  echo %date_%
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  set date_=

A second method is given below. In this alternative the date is
stored in the %date_% environment variable in the eight-character
format. It also gets the current time.
  @echo off
  ::
  rem Create a temporary batch file to set the date
  echo @echo off> date$$$$.bat
  echo set date_=%%3>> date$$$$.bat
  echo set time_=%%4>> date$$$$.bat
  ::
  rem Create another batch which executes the first
  dir date$$$$.bat | find "DATE$$$" > tmp$$$.bat
  call tmp$$$
  ::
  rem Delete the temporary batch files
  if exist tmp$$$.bat del tmp$$$.bat
  if exist date$$$$.bat del date$$$$.bat
  ::
  rem Test it
  echo %date_%
  echo %time_%

For a third method that puts the date into the %date_% environment
variable see e.g. the item "How can I backup from the current
directory files made today?" of this batch FAQ collection. It
produces the ten-character format.

Let's return to the first method. Batch savant Tom Lavedas often
uses the following kind of style.
  @echo off
  echo @prompt set Date=$d$_set Time=$t$h$h$h> {a}.bat
  %comspec% /e:2048 /c {a}.bat > {b}.bat
  for %%v in ({b}.bat del) do call %%v {?}.bat
  echo The date is %Date% and the time is %Time%
Let's study closer the logic of this batch. There is much to learn
from the details.

  1) @echo off
     - turns off echoing the commands on the screen
  2) echo @prompt set Date=$d$_set Time=$t$h$h$h> {a}.bat
     - creates auxiliary batch {a}.bat and puts there
       @prompt set Date=$d$_set Time=$t$h$h$h
     - $d displays the current date in the prompt
     - $_ enters a linefeed in between
     - $t displays the current time in the prompt
     - $h$h$h deletes the last three characters,in this case the
              superfluous hundreds of the second)
  3) %comspec% /e:2048 /c {a}.bat > {b}.bat
     - runs {a} and put the results into another auxiliary batch
       {b}.bat whose contents will be e.g.
         set Date=Sat 11/07/1998
         set Time=14:07:03
     - /e:2048 reserves (usually) enough environment space
     - /c tells the command interpreter to exit after performing
          the task. This avoids multiple shells.
  4) for %%v in ({b}.bat del) do call %%v {?}.bat
     - this is "shorthand" for
         call {b}.bat
         del {?}.bat  %which deletes both {a}.bat and {b}.bat%
  5) echo The date is %Date% and the time is %Time%
     - displays the result

Another method is creating a new file and taking the date
information from there. The advantage is that a directory compatible
date format is ensured.
  @echo off
  echon set date_=>tmp$$$.bat
  dir tmp$$$.bat|find "TMP$$$   BAT"|cut 28 35>>tmp$$$.bat
  call tmp$$$
  echo date_=%date_%
  set date_=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

A quick and dirty method for getting just the year would be
  @echo off
  set yy_=0000
  echo.|date|find "2000"
  if not errorlevel==1 set yy_=2000
  echo.|date|find "2001"
  if not errorlevel==1 set yy_=2001
  echo.|date|find "2002"
  if not errorlevel==1 set yy_=2002
  echo.|date|find "2002"
  if not errorlevel==1 set yy_=2003
  echo The system year is %yy_%

There are so many options. For much more, see the related, further
refined later items #59 "How get today's date elements into a
environment variables?" and #116 "How can I get yesterday's date
into an environment variable?" for yet another method for getting
the date.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:36 2002
Subject: Identifying the individual PC
Date: Fri, 1 Mar 2002 01:00:36
From: ts@uwasa.fi (Timo Salmi)

35. A tip for power users. Identifying the individual PC.
=========================================================

In cases of some batches it is useful to identify the PC the batch
is run on. For example I have several different PCs myself and
occasionally I need to differentiate between them. The solution is
really trivial. Set an environment variable in the autoexec.bat to
designate the PC. I use a variable pcid_ for this purpose. An (old)
outline batch illustrates.
  @echo off
  if "%pcid_%"=="" goto _none
  goto %pcid_%
  :dell
  echo Dell 325N laptop, do whatever
  goto _out
  :trifu
  echo Trifunic 386 desktop, do whatever
  goto _out
  :karvi
  echo "Garfunkel" Pinus 486 desktop, do whatever
  goto _out
  :_none
  echo PC not identified, do whatever
  :_out
For example in the autoexec.bat of my DELL 325N laptop I have
  set pcid_=dell
You'll see this identification trick used e.g. in the BOOT.BAT batch
of the collection at hand.

Another, similar trick I employ is standardizing the ram disk
reference by applying in my AUTOEXEC.BAT e.g.
  set ram=m
This I can always refer in my batches as %ram% to the ram disk
letter irrespective of which of my PCs I am using. You'll see this
trick used in some of the individual batch files of this collection.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:37 2002
Subject: For loop and redirection
Date: Fri, 1 Mar 2002 01:00:37
From: ts@uwasa.fi (Timo Salmi)

36. For loop and redirection quirks
===================================

A question from the Usenet newsgroups news:comp.os.msdos.misc and
news:comp.os.msdos.programmer:
> I am using DOS 5.0 and I have the following line in my batch file:
> for %%f in (a b c d) do if exist %%f echo put %%f >> tmpfile
> where a,b,c,d are some filenames.
>
> What I expect it to do is to echo the lines
>  put a
>  put b
>  put c
>  put d
> into the file tmpfile.
>
> But what happen is after the "put a" is written to tmpfile, the rest
> of the lines will just echo to the screen, look like that the
> redirection is not working.
>
> If I take away the "if exist" everything is working fine.  I found
> out every time when I use a conditional statement with redirection,
> it will only redirect the first time, the rest will echo to the
> screen.
>
> Is it the for loop cannot be mixed with the conditional statement
> and the redirection?

Yes, it can be mixed, but not so simply in this case. Use the
following batch
  @echo off
  del tmpfile
  for %%f in (a b c d) do if exist %%f call auxil %%f
where auxil.bat contains
  @echo off
  echo put %1>> tmpfile
I'll be darned if I know why :-).

In fact it is possible to do this with a single batch by employing
the following method described in an earlier item
  @echo off
  echo @prompt echo put %%%%1$g$g tmpfile> tmp$$$.bat
  %comspec% /c tmp$$$> auxil.bat
  if exist tmpfile del tmpfile
  for %%f in (a b c d) do if exist %%f call auxil %%f
  del tmp$$$.bat
  del auxil.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:38 2002
Subject: Traversing a directory tree
Date: Fri, 1 Mar 2002 01:00:38
From: ts@uwasa.fi (Timo Salmi)

37. Is it possible to traverse a directory tree with a batch?
=============================================================

Traversing it straight up is relatively easy as can be seen from
this example.
  @echo off
  :_loop
  dir/w
  if not exist ..\nul goto _out
  cd ..
  goto _loop
  :_out

Going recursively down through a directory and its subdirectories is
very complicated. Yet it can be done if you have MS-DOS 5.00 or
beyond. The SWEEP.BAT batch accompanying tsbat*.zip demonstrates
how. The method is, however, too difficult to be of real practical
importance.

It is easier to use an auxiliary program for sweeping, like
TARGET.EXE ftp://garbo.uwasa.fi/pc/filefind/target15.zip or
SWEEP.COM from ftp://garbo.uwasa.fi/pc/pcmagvol/vol4n24.zip.
Especially TARGET.EXE is a very versatile tool. Or, one case use
G(nu)AWK as follows:
  dir c:\xxx\yyy\*.*/a:-d/s/b|gawk '{printf"WhateverCommand %%s\n",$0}'
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:39 2002
Subject: Echoing the > and >> symbols
Date: Fri, 1 Mar 2002 01:00:39
From: ts@uwasa.fi (Timo Salmi)

38. Is it possible to echo the redirection symbol in a batch?
=============================================================

In certain situations would be useful to be able echo the
redirection symbol rather than have its perform its redirection
function. For example your batch file might have a help line like
this
  echo The line to customize is "echo dir/w %%%%3\%%2 >> %%new_%%"
As you see the double quotes pre-empt the redirection. If you left
them out, the line would result create a file %NEW_% containing
"The line to customize is echo dir/w %%3\%2".
   Contrary to Unix, \ cannot be used to cover the special meaning
of a symbol. As explained in the item "The quote character %" the %
sign, can as is demonstrated above by the %% pairs. But %> does not
take precedence over the redirection.
   Tom Lavedas posted a useful example on the Usenet news which
shows additional trick in the the echoing to the screen. The
solution goes like this:
  @echo off
  echo "^HThis is a pair of greater than signs >>"^H.
In the above the ^H means the backspace character which you can
input e.g. in MS-DOS Edit by entering ctrl-P H . An additional
aspect of this solution is that the last ^H must be followed by at
least one character. Otherwise the last " will be sent to the
console.

 Q2: I want to send the character | to a batch to form a batch file.
For instance when I try the following
     echo dir /w /p | find "Vol" >>file.bat
I get a file.bat file that contains nothing. How can I solve this?

 A2: This problem can be solved by using QBASIC which comes with
e.g. MS-DOS version 6.22. Below is the batch that does what is being
asked for. The crucial trick is to use variables for the characters
which batches cannot redirect. Below b$ contains |, i.e. the ASCII
character 124. (q$ contains the quote " character.)
  @echo off
  >  tmp$$$.bas echo LET q$ = CHR$(34)
  >> tmp$$$.bas echo LET b$ = CHR$(124)
  >> tmp$$$.bas echo OPEN "file.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "dir /w "; b$; " find "; q$; "Volume"; q$
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  qbasic /run tmp$$$.bas
  del tmp$$$.bas
P.S. The /w switch and especially the /p switch in the question are
superfluous.

Another, sinpler option is using the SED Unix port. The solution
then is
  echo dir /w /p I find "Vol"|sed -e "s/I/\x7C/" >>file.bat
The above is based on the fact that \xHH can be used to enter hex
code in SED. This, naturally, opens quite a realm for possibilities.
The same method is available in G(nu)AWK. Below is a simple
demonstration to run
  @echo off
  echo.|gawk '{printf "\x3E \x7C \x3C \n"}'
  echo Just a test|gawk '{printf "%%s \x3E \x7C \x3C \n",$0}'

There also is a prompt-base solution as pointed out by Tom Lavedas:
"It can also be solved using the characteristics of the PROMPT
command, specifically the dollar sign substitution character '$B',
something like this ...
  echo @prompt dir /w /p $b find "Vol" > {t}.bat
  %comspec% /e:2048 /c {t}.bat >> file.bat
  del {t}.bat
BTW, I usually can find a way around needing such a construction,
often with less programming overhead."

This is Timo again. Let's look at preparing a simple HTML file with
a batch. In HTML there are a lot of "<" and ">" symbols involved.
Batches will mistake them for redirection symbols unless the tricks
of this item are utilized. For example

  @echo off

  echo @prompt $lHTML$g > tmp$$$.bat
  %comspec% /e:2048 /c tmp$$$.bat > test.htm

  echo @prompt $lHEAD$g$lTITLE$gTest$l/TITLE$g$l/HEAD$g > tmp$$$.bat
  %comspec% /e:2048 /c tmp$$$.bat >> test.htm

  echo @prompt Whatever$lP$g$lHR$g > tmp$$$.bat
  %comspec% /e:2048 /c tmp$$$.bat >> test.htm

  echo @prompt $l/BODY$g > tmp$$$.bat
  %comspec% /e:2048 /c tmp$$$.bat >> test.htm

  echo @prompt $l/HTML$g > tmp$$$.bat
  %comspec% /e:2048 /c tmp$$$.bat >> test.htm

  del tmp$$$.bat
  dir test.htm

Using SED, e.g.
  @echo off
  > tmp$$$.htm echo ((HTML))
  >>tmp$$$.htm echo ((HEAD))((TITLE))Test((/TITLE))((/HEAD))
  >>tmp$$$.htm echo Whatever((P))((HR))
  >>tmp$$$.htm echo ((/BODY))
  >>tmp$$$.htm echo ((/HTML))
  sed "s/((/\x3C/g;s/))/\x3E/g" < tmp$$$.htm > con
  for %%f in (tmp$$$.htm) do if exist %%f del %%f
The "> con" is optional.

Using G(nu)AWK (more cumbersome, I think):
  @echo off
  echo.|gawk '{printf "\x3CHTML\x3E\n"}'
  echo.|gawk '{printf "\x3CHEAD\x3E\x3CTITLE\x3ETest\x3C/TITLE\x3E\x3C/HEAD\x3E\n"}'
  echo.|gawk '{printf "Whatever\x3CP\x3E\x3CHR\x3E\n"}'
  echo.|gawk '{printf "\x3C/BODY\x3E\n"}'
  echo.|gawk '{printf "\x3C/HTML\x3E\n"}'

There is a very inventive alternative that was brought up by Benny
Pedersen on the Usenet news. Adapted below with minor chages. Note
that in calling this batch you'll have to include the .bat
extension.
  @echo off
  @goto batch
  <HTML>
  <HEAD><TITLE>Test</TITLE></HEAD>
  Whatever<P><HR>
  </BODY>
  </HTML>
  :batch @
  find /v "@" <%0 > test.htm
  @dir test.htm
This is a nice trick which obviously has potential in other tasks as
well. (See item #7.)

What if you wish to ensure that the .bat extension always is there
without having to worry about it in calling the batch. This does the
trick:
  @echo off
  @echo %0 | find /i ".bat" > nul
  @set prog=%0
  @if errorlevel==1 set prog=%0.bat
  @goto batch
  <HTML>
  <HEAD><TITLE>Test</TITLE></HEAD>
  Whatever<P><HR>
  </BODY>
  </HTML>
  :batch @
  find /v "@" <%prog% > test.htm
  @dir test.htm
  @set prog=

Another option is requiring the .BAT extension in the call and
checking that it is present. The skeleton of this method is below:
  @echo off
  if not exist %0 if exist %0.bat goto _nobext
  echo Whatever
  goto _out
  ::
  :_nobext
  echo The .BAT extension required in calling %0
  ::
  :_out

Let take yet another, important example. Say you wish to print out
the line like
<?xml version="1.0"?><!DOCTYPE ABC SYSTEM "http://abc.com/"><ABC/>
This is how it is done:
  @echo off
  > tmp$$$.awk echo {
  >>tmp$$$.awk echo  printf "<?xml version=\"1.0\"?>"
  >>tmp$$$.awk echo  printf "<!DOCTYPE ABC SYSTEM \"http://abc.com/\">"
  >>tmp$$$.awk echo  printf "<ABC/>"
  >>tmp$$$.awk echo  printf "\n"
  >>tmp$$$.awk echo }
  echo.|awk -f tmp$$$.awk>myfile.htm
  del tmp$$$.awk
The above is a generic solution that gives you the option of generic
production of all the characters, including the redirection symbols.
Note that you have to "quote" the quotes with a backslash \ within
the statements. The example also shows how you can divide the
reproduction on several lines.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:40 2002
Subject: Getting the file basename
Date: Fri, 1 Mar 2002 01:00:40
From: ts@uwasa.fi (Timo Salmi)

39. How can I extract the file basename?
========================================

Occasionally one needs to get the file name without the extension.
Just like getting the extension from a file name using the "for %%f
in (/%exten_%)" trick, even this can be done with batch commands
only. The batch code for getting the basename has been presented by
Neil Rubenking in PC Magazine April 26, 1994, Vol. 13, No. 8, pp.
275-276. But enough is enough even with batch tricks. The logic is
getting overly complicated. One has to draw the line somewhere, stop
kidding oneself, and start using batch enhancers (external programs
to help out). I think here the limit has been reached. Hence I have
included "basename" and "basepath" programs, which you can use to
create the enhancers. They return the relevant information into an
environment variable with that name.
   When you come to think of it. From one viewpoint, what else than
batch enhancers are all the external MS-DOS commands (usually) in
your C:\DOS directory?
   Using basename and basepath is very easy. Below is an example
       @echo off
       basename r:\cmand\command.com
       basepath r:\cmand\command.com
       echo %basename%
       echo %basepath%
You can discard the environment variable simply by applying (note
the two alternatives):
       set basename=
       basepath
There is also a "basexten" batch enhancer in the /pc/ts/tsbat*.zip
collection.
   A batch-only solution is presented below. It owes heavily to a
posting by Ted Davis on the Usenet news
  @echo off
  ::
  rem Instructions
  if "%1"=="" goto _usage
  ::
  rem Create an auxiliary directory
  mkdir tmpaux$$
  ::
  rem Create an empty auxiliary file
  rem > tmpaux$$\%1
  ::
  rem Go to the auxiliary directory
  cd tmpaux$$
  ::
  rem Rename the file without extension (this is the trick!)
  ren %1 *.
  ::
  rem Get the new file name into an environment variable
  for %%f in (*) do set basename=%%f
  ::
  rem Let's test it
  echo The basename is %basename%
  ::
  rem Back to the original directory
  cd ..
  ::
  rem Delete the auxiliary file
  echo.| del tmpaux$$\*
  ::
  rem Delete the auxiliary directory
  rmdir tmpaux$$
  ::
  rem Delete the environment variable
  set basename=
  ::
  goto _end
  ::
  :_usage
  echo Usage: %0 [FileNameWithExtension]
  :_end

Another batch-only alternative would be to utilize the BASIC
interpreters that come with MS-DOS.
  @echo off
  ::
  :: If no parameter is given to the batch, show its proper usage
  if "%1"=="" goto _help
  ::
  :: Prepare a QBASIC program to extract the base from a filename
  :: Handle different file name types like NAME.DAT NAME and .DAT
  echo LET a$="%1" > tmp$$$.bas
  echo LET p = INSTR(1, a$, ".") - 1 >> tmp$$$.bas
  :: Case of only extension, no basename
  echo IF p = 0 THEN LET a$ = "" >> tmp$$$.bas
  :: Other cases
  echo IF NOT (p=-1) AND NOT (p=0) THEN LET a$ = MID$(a$, 1, p) >> tmp$$$.bas
  echo OPEN "tmp###.bat" FOR OUTPUT AS #1 >> tmp$$$.bas
  echo PRINT #1, "@echo off" >> tmp$$$.bas
  echo PRINT #1, "set basenam_=" + a$ >> tmp$$$.bas
  echo SYSTEM >> tmp$$$.bas
  qbasic /run tmp$$$.bas
  ::
  :: Call the batch created by the QBASIC program
  call tmp###.bat
  ::
  :: Demonstrate that the result is in the basenam_ environment variable
  if not "%basenam_%"=="" echo The basename of %1 is %basenam_%
  if "%basenam_%"=="" echo The basename of %1 is empty
  goto _end
  ::
  :_help
  echo Usage %0 [FullFileName]
  ::
  :: Cleanup
  :_end
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  set basenam_=

The G(nu)AWK Unix port can be used to extract parts of the path and
file name. The lines
  echo %1|gawk -F: '{print $NF}'>tmp$$$.txt
  type tmp$$$.txt|gawk -F. '{print $1}'|gawk -F\ '{printf "@set bn_=%%s\n",$NF}'>tmp$$$.bat
extract the basename. A complete test batch would be
  @echo off
  ::
  :: Usage
  if "%1"=="" goto _help
  ::
  :: Set the basename
  echo %1|gawk -F: '{print $NF}'>tmp$$$.txt
  type tmp$$$.txt|gawk -F. '{print $1}'|gawk -F\ '{printf "@set bn_=%%s\n",$NF}'>tmp$$$.bat
  call tmp$$$.bat
  ::
  :: Test it
  echo %bn_%
  goto _out
  ::
  :: Help
  :_help
  echo Usage %0 [FileNameWithPath]
  ::
  :: Clean up
  :_out
  set bn_=
  for %%f in (tmp$$$.bat tmp$$$.txt) do if exist %%f del %%f
If you just need to omit the path from the file name, then use
  echo %1|gawk -F: '{print $NF}'|gawk -F\ '{printf "%%s\n",$NF}'
On the other hand, if you wish to get the path, i.e. omit the file
name, insert into the test batch:
  ::
  :: Extract the the path
  echo %1|sed -e "s/%bn_%$//"

One further alternative for these problems is a reasonably simple
SED solution:
  @echo off
  if "%1"=="" goto _usage
  echo The filename, any path stripped:
  echo %1|sed "s/^.*[\\]//"
  echo The basename (any path stripped)
  echo %1|sed "s/^.*[\\]//;s/\..*//"
  echo The extension
  echo %1|sed "s/^.*[\\]//;s/[^\.]*//;s/\.//"
  goto _end
  :_usage
  echo Usage %0 FileName
  :_end
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:41 2002
Subject: Reading user's input
Date: Fri, 1 Mar 2002 01:00:41
From: ts@uwasa.fi (Timo Salmi)

40. A batch to put user input into an environment variable
==========================================================

This definitely must be the neatest batch programming trick I have
ever seen. It is an adaptation of the batch published in Neil J.
Rubenking's User-to-User column in PC Magazine June 27, 1995, Vol.
14, No. 12, pp. 247-248. It is based on an idea by Tom Lavedas. It
will remedy the biggest (alleged) flaw in MS-DOS batch programming,
that is getting user input without any external programs. My
adaptation puts a simple word input into the INPUT_ environment
variable. The original inputs a whole sentence, but it is more
complicated. A single word (or a single letter) input is what is
usually sufficient in batch programming. The essence of the trick,
of you want to study it carefully to understand it, is in the fact
that the time command outputs the word "Enter" which features as an
auxiliary batch to be run by the SETINPUT.BAT below. An MS-DOS
version 3.3 or later is required.
  @echo off
  rem INPUT.BAT
  echo This will copy your input to the environment variable INPUT_
  echo Give your input:
  fc con nul /lb1 /n | time | find "    1:  "> setinput.bat
  echo @echo off> enter.bat
  echo set input_=%%4>> enter.bat
  call setinput
  del setinput.bat
  del enter.bat
  echo The value of INPUT_=%input_%
The /LB switch in the above with value 1 sets to one the number of
lines for the internal line buffer. The /N switch "displays the line
numbers during an ASCII comparison".
   For more on this approach see Tom's WWW page "Input in a Running
Batch File" http://www.pressroom.com/~tglbatch/input.htm
   This is also a suitable spot to show the usage of the MS-DOS
CHOICE.COM input command with the following example. Note that the
order of the errorlevel tests is significant and should be retained
in the order used below.
  @echo off
  choice /c:ync /t:c,10 "Respond within 10 seconds (else C is assumed) "
  if errorlevel 3 goto _cancel
  if errorlevel 2 goto _no
  if errorlevel 1 goto _yes
  if errorlevel 0 goto _break
  echo Error! This should not happen.
  goto _end
  :_break
  echo Break was pressed
  goto _end
  :_yes
  echo Y was pressed
  goto _end
  :_no
  echo N was pressed
  goto _end
  :_cancel
  echo C was pressed, or no response within 10 seconds
  :_end
For an explanation of the CHOICE.COM switches apply "HELP CHOICE" if
you have MS-DOS 6.22. Also see the earlier item #23 for another,
perhaps even a more instructive demonstration of the choice logic.

 A2: It is instructive to note that batches often can be improved
since there is some hitch. In this case Larry Weiss observed that
"If the user enters only the Return-key, then the bat file will
reset the system time to 1:00 am. There is no warning about this
unfortunate side-effect. That bat file can be re-written to use
'date' instead of 'time' (replacing the reference to %%4 with %%5)
as:
  @echo off
  rem INPUT.BAT
  echo This will copy your input to the environment variable INPUT_
  echo Give your input:
  fc con nul /lb1 /n | date | find "    1:  "> setinput.bat
  echo @echo off> enter.bat
  echo set input_=%%5>> enter.bat
  call setinput
  del setinput.bat
  del enter.bat
  echo The value of INPUT_=%input_%
With those changes, there is no unexpected disturbance of either the
date nor time when no input is provided."

 A3: Another, easier approach to this problem is using a QBASIC
solution. QBASIC comes with e.g. MS-DOS version 6.22.
  @echo off
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  >  tmp$$$.bas echo PRINT "Give your input";
  >> tmp$$$.bas echo INPUT a$
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "set input=";a$
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program prepared in the above
  qbasic /run tmp$$$.bas
  ::
  :: Call the tmp###.bat batch created by the tmp$$$.bas QBASIC program
  call tmp###.bat
  ::
  :: Delete the auxiliary files
  del tmp$$$.bas
  del tmp###.bat
  ::
  :: Test the result
  echo Your input was %input%

Another, similar alternative is trusting your user a bit more and
use this "poor man's" solution
  @echo off
  echo rem Write your input on the line below; then ALT-F X>tmp$$$.bat
  echo @set input_=>>tmp$$$.bat
  edit tmp$$$.bat
  call tmp$$$.bat
  ::
  :: Show the result and clean up
  echo Your input was: %input_%
  set input_=
  if exist tmp$$$.bat del tmp$$$.bat

Using SED:
  @echo off
  echo Give your input:
  fc con nul /lb1/n|find "    1:  "|sed "s/    1:  /@set input_=/">setinput.bat
  call setinput.bat
  echo The value of INPUT_=%input_%
  for %%f in (setinput.bat) do if exist %%f del %%f
  set input_=

Using debug (the idea is due to William Allen). Note that the input
environment variable must be exactly three characters long. Before
you use it, or any other debug scripts, carefully read item #136!
  @echo off
  echo Give your input:
  fc /lb1 /n con nul | find "1:">tmp$$$.bat
  echo.exit|%comspec%/k prompt e100'set in_='$_w$_q|debug tmp$$$.bat>nul
  call tmp$$$
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  echo The value of in_=%in_%
  set in_=

Yet another method for getting the user's input. This one uses two
third-party programs, echon (echoing without the linefeed <CR><LF>)
given in item #99 and CUT.EXE (Extract or delete text columns).
There is a compiled ECHON.EXE in this tsbat package. You'll probably
realize that these two simple enhancer programs have a quite
potential in solving some otherwise tricky batch tasks.
  @echo off
  ::
  :: Put the input into a file. Cut superfluous leading columns.
  echo Give your input:
  fc con nul /lb1/n|find "    1:  "|cut 9 255>tmp$$$.txt
  ::
  :: Make a batch to set an input_ environment variable
  echon @set input_=>tmp$$$.bat
  type tmp$$$.txt>>tmp$$$.bat
  call tmp$$$
  ::
  :: Demonstrate the result
  if not "%input_%"=="" echo Your input was: %input_%
  if "%input_%"=="" echo Empty input
  ::
  :: Clean up
  set input_=
  for %%f in (tmp$$$.txt tmp$$$.bat) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:42 2002
Subject: Getting the last input parameter
Date: Fri, 1 Mar 2002 01:00:42
From: ts@uwasa.fi (Timo Salmi)

41. How can I get the last replaceable parameter given to a batch?
==================================================================

As we know, batch files can use the so-called replaceable parameters
from %0 to %9. It is easy to get the first parameter in a batch
call. It is %1. (%0 gives the batch name). But what about getting
the last parameter when you do not know in advance how many
parameters the call has. Below is the code. It puts the last
parameter in the environment variable NF_.
  @echo off
  set nf_=
  :_loop
    if "%1"=="" goto _end
    set nf_=%1
    shift
    goto _loop
  :_end
  rem Let's test the result
  if not "%nf_%"=="" echo %nf_%
  if "%nf_%"=="" echo No parameters were given to the batch
Note a catch. After you apply the above, you no longer can access
the original parameters. If you need them later in the batch, you
have to store them at the outset of the batch file, e.g. like this
  set bname_=%0
  set a_=%1
  set b_=%2
  set c_=%3
John Savage points out that the code for getting the last parameter
can be made simpler:
  @echo off
  :_loop
  shift
  if not "%1"=="" goto _loop
  echo last parameter is "%0"
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:43 2002
Subject: Creating an empty file
Date: Fri, 1 Mar 2002 01:00:43
From: ts@uwasa.fi (Timo Salmi)

42. Creating an empty file if the file does not already exist
=============================================================

@echo off
if exist testfile goto _nocreate    %Don't destroy an existing file%
rem > testfile
:_nocreate
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:44 2002
Subject: Handling disk's serial number
Date: Fri, 1 Mar 2002 01:00:44
From: ts@uwasa.fi (Timo Salmi)

43. How can I change or remove the disk volume serial number?
=============================================================

Since version 4.0 the disk serial number was added to MS-DOS. When
you format a disk is it given a number like 4132-1DFF. It is
possible to change that information with an innovative batch file
alone. The batch by Bruce W. Shumway can be found in the PC Magazine
April 23, 1996, Vol. 15 No. 8, pp. 221-222. There also is a program
SETSER.EXE "Set the disk's serial number" by yours truly in the
ftp://garbo.uwasa.fi/pc/ts/ts5dos11.zip collection.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:45 2002
Subject: Inserting a delay
Date: Fri, 1 Mar 2002 01:00:45
From: ts@uwasa.fi (Timo Salmi)

44. How to pause in a batch for a preset number of seconds?
===========================================================

You can use the MS-DOS CHOICE.COM command for the purpose as the
example below demonstrates
  @echo off
  echo Testing a delay, starting at ...
  echo.| time | find /v "new"
  choice /c:. /t:.,5 /n Pausing for five seconds
  echo ending at ...
  echo.| time | find /v "new"
The choice command was introduced with MS-DOS 6. If you have an
earlier MS-DOS version you can use my similar CHOOSE.EXE from
ftp://garbo.uwasa.fi/pc/ts/tsutlf16.zip.
   For pauses longer than 99 seconds see the item #64. For better
understanding the CHOICE parameter values, see the end of item #40.

Tom Lavedas points out that if one uses
  type nul | choice /c:. /t:.,5 /n Pausing for five seconds
"The piping of the output from the TYPE command into CHOICE acts to
defeat keyboard entry for the wait period."

Also see the later item "How can I write a "SLEEP" command to pause
for a certain time?" for more on this question.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:46 2002
Subject: Batch compilers
Date: Fri, 1 Mar 2002 01:00:46
From: ts@uwasa.fi (Timo Salmi)

45. Where can I find a program to compile batches into COMs or EXEs?
====================================================================

Since this question is so frequently asked I'll include an answer
into my 1batfaq.txt information file. From Garbo program archive's
MS-DOS index file ftp://garbo.uwasa.fi/pc/INDEX.ZIP we can readily
locate the following files. Personally, I am dubious about batch
compilers and their complications. In my opinion, if one wants
distribute executables, it is much better to use a genuine
programming language like Turbo Pascal or C. I prefer batches in
their regular source format.

 37419 Aug 10 1991 ftp://garbo.uwasa.fi/pc/pcmagutl/bat2ex15.zip
 bat2ex15.zip Compile batch files to be executables, PC-Mag update

 51299 Oct 31 1994 ftp://garbo.uwasa.fi/pc/batchutil/tbt324.zip
 tbt324.zip TurboBAT Batch File Compiler, Foley Hi-Tech Systems
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:47 2002
Subject: Is a disk empty?
Date: Fri, 1 Mar 2002 01:00:47
From: ts@uwasa.fi (Timo Salmi)

46. How can I test whether a disk is empty or not?
==================================================

This question is best answered by an annotated batch source:

  @echo off
  rem Provide help if no parameter is given.
  if "%1"=="" goto _usage

  rem Check that the input syntax was acceptable.
  for %%f in (a b A B) do if "%1"=="%%f" goto _label1
  goto _usage

  rem See if "bytes free" appears in the directory listing as it will
  rem if and only if there are files in the directory.
  :_label1
  dir %1:\ /s | find "bytes free" > found.$$$

  rem If "bytes free" was not found found.$$$ file will be empty.
  rem An empty file will not be copied.
  copy found.$$$ notempty.$$$ > nul

  rem If found.$$$ was empty is was not copied, use this fact to test.
  if exist notempty.$$$ echo Disk %1: is not empty
  if not exist notempty.$$$ echo Disk %1: is empty

  rem Delete the auxiliary files.
  if exist found.$$$ del found.$$$
  if exist notempty.$$$ del notempty.$$$
  goto _out

  :_usage
  echo Usage: %0 [DriveLetter]
  echo e.g. %0 A
  echo Put no colon (:) after the DriveLetter!

  :_out

The volume where you drive this from must not be write protected.

Much the same method can be used to check if a directory contains
files or not:
  @echo off
  ::
  rem Help, if necessary
  if "%1"=="" goto _help
  ::
  rem Check if directory contains any empty files
  dir %1 | find " 0 " | find /v " bytes" > found.$$$
  copy found.$$$ notempty.$$$ > nul
  if exist notempty.$$$ echo Directory %1 contains files
  if exist notempty.$$$ goto _clean
  ::
  rem Check whether the directory contains files or not
  dir %1 | find " 0 bytes" > found.$$$
  copy found.$$$ empty.$$$ > nul
  if exist empty.$$$ echo Directory %1 contains no files
  if exist empty.$$$ goto _clean
  echo Directory %1 contains files
  ::
  :_clean
  rem Clean up
  del found.$$$
  if exist empty.$$$ del empty.$$$
  if exist notempty.$$$ del notempty.$$$
  goto _end
  ::
  :_help
  echo Usage: %0 DirectoryPath
  :_end
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:48 2002
Subject: A different batch each day
Date: Fri, 1 Mar 2002 01:00:48
From: ts@uwasa.fi (Timo Salmi)

47. How can I run a different batch depending on the weekday?
=============================================================

  @echo off
  ::
  rem Let's take care of everything by a single batch file.
  rem First prepare a batch for each day. For brevity let's prepare
  rem them for Monday and Tuesday days only.
  ::
  echo echo It is Monday today and whatever else you may wish to do>mon.bat
  echo echo It is Tuesday today and whatever else you may wish to do>tue.bat
  ::
  rem The date command produces output like
  rem Current date is Tue 27/08/1996
  rem Enter new date (dd-mm-yy):
  ::
  rem Utilize this fact by making a batch file tmp$$$.bat which in turn
  rem runs the batch which is the third parameter (%3) of current.bat
  rem (Tue.bat in the above).
  echo.| date | find "Current">tmp$$$.bat
  echo call %%3>current.bat
  call tmp$$$.bat
  ::
  rem Let's delete the tmp$$$.bat and current.bat files.
  if exist current.bat del current.bat
  if exist tmp$$$.bat del tmp$$$.bat
  ::
  rem Since this was only a test, let's delete all the daily files, too.
  if exist mon.bat del mon.bat
  if exist tue.bat del tue.bat

As usual, there are several options for solving a task. Below is one
of the many methods for getting the weekday into an environment
variable.
  @echo off
  echo.|date|find "Mon" >nul
  if errorlevel==0 if not errorlevel==1 set wkday_=Mon
  echo.|date|find "Tue" >nul
  if errorlevel==0 if not errorlevel==1 set wkday_=Tue
  echo.|date|find "Wed" >nul
  if errorlevel==0 if not errorlevel==1 set wkday_=Wed
  echo.|date|find "Thu" >nul
  if errorlevel==0 if not errorlevel==1 set wkday_=Thu
  echo.|date|find "Fri" >nul
  if errorlevel==0 if not errorlevel==1 set wkday_=Fri
  echo.|date|find "Sat" >nul
  if errorlevel==0 if not errorlevel==1 set wkday_=Sat
  echo.|date|find "Sun" >nul
  if errorlevel==0 if not errorlevel==1 set wkday_=Sun
  echo %wkday_%
  set wkday_=

Yet another option is to utilize a unix port like G(nu)AWK. Note
that the second and third like must be combined to make a single
line. They are presented separately below just for the reasons of
wrapping.
  @echo off
  echo.|date|gawk 'NR==1 { printf "set date_=%%s\n",$4 }' > tmp$$$.bat
  call tmp$$$.bat
  echo %date_%
  set date_=
  if exist tmp$$$.bat del tmp$$$.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:49 2002
Subject: Line numbers
Date: Fri, 1 Mar 2002 01:00:49
From: ts@uwasa.fi (Timo Salmi)

48. Can one put line numbers into a file with just batch commands?
==================================================================

The essence of this item is from postings on the Usenet news by John
Savage. The trick is
 FC /N nul file.txt > file2.txt
The number of lines is limited to a hundred lines. You can increase
that to a maximum of 222 lines using
 FC /N /LB222 nul file.txt
As you see, there is some redundancy in this batch FAQ, since there
are so many items. This question was already tackled in item #32 but
using a different (a FIND) method:
  find /v/n "" file.txt
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:50 2002
Subject: Backup new files
Date: Fri, 1 Mar 2002 01:00:50
From: ts@uwasa.fi (Timo Salmi)

49. How can I backup from the current directory files made today?
=================================================================

Here is the trick using XCOPY tested on a PC running MS-DOS 5.0.
  @echo off
  rem Get the date into an environment variable
  echo.| date | find "Current" > tmp$$$.bat
  echo set date_=%%4> current.bat
  call tmp$$$
  if exist tmp$$$.bat del tmp$$$.bat
  if exist current.bat del current.bat
  ::
  rem Copy the files created or updated today
  xcopy *.* a:\ /D:%date_% /P /V
  if errorlevel 0 goto _out
  echo An XCOPY error has occurred
  ::
  :_out
  set date_=
The country-dependent XCOPY parameter /D:date copies files modified
on or after the given date. The /P prompts for confirmation. The /V
parameter sets copy verification on.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:51 2002
Subject: Traversing all files
Date: Fri, 1 Mar 2002 01:00:51
From: ts@uwasa.fi (Timo Salmi)

50. How can I traverse all files of a directory in a batch?
===========================================================

See REPEAT.BAT of my TSBAT*.ZIP batch files collection.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:52 2002
Subject: Debugging a batch
Date: Fri, 1 Mar 2002 01:00:52
From: ts@uwasa.fi (Timo Salmi)

51. How can I step through a batch a command at a time to debug it?
===================================================================

This question is answered by MS-DOS "HELP COMMAND". Take a look. The
essence of the method is this: "command /y /c mybatch.bat". This
lets you traverse your batch one command at a time. Another trick is
to use REM and GOTO commands to help locate when the error occurs
and when not. It has the advantage of letting you choose the parts,
which you wish to concentrate on. Yet another method is to place
PAUSE commands near the problem spots to identify which line causes
the error.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:53 2002
Subject: Display all files updated today
Date: Fri, 1 Mar 2002 01:00:53
From: ts@uwasa.fi (Timo Salmi)

52. How to display all files made or updated on a day or today?
===============================================================

To find the answer please study DATEDIR.BAT.

One, rather similar, and simpler outline (e.g. for C:) is
  @echo off
  echon set date_=>tmp$$$.bat
  dir tmp$$$.bat|find "TMP$$$   BAT"|cut 28 35>>tmp$$$.bat
  call tmp$$$
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  dir c:\*.* /s|find "%date_%"
  set date_=
The problem is that the file path is not shown.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:54 2002
Subject: Listing all files
Date: Fri, 1 Mar 2002 01:00:54
From: ts@uwasa.fi (Timo Salmi)

53. How can I make a list of all my files and locate a certain file?
====================================================================

Let's assume that you have three three partitions, the drives C, D
and E. To make an ordered list of all your files you can apply e.g.
  @echo off
  if exist myfiles.lst del myfiles.lst
  for %%f in (c d e) do dir /w /o:en /s %%f:\*.* >> myfiles.lst
To be able to find a certain file add
  edit myfiles.lst
The /W parameter displays the results of DIR in a wide format. The
/O:EN sort the results by extension and name. The /S parameter
traverses also all the subdirectories.

Another option which lists all your files, this time with with paths
included, but without the ordering, is
  @echo off
  if exist myfiles.tmp del myfiles.tmp
  for %%f in (c d e) do chkdsk /v %%f: >> myfiles.tmp
  type myfiles.tmp | find "\" | find /v "Directory " > myfiles.lst
  del myfiles.tmp
To be able to find a specific file on your harddisk add
  type myfiles.lst | find /i "%1"
The FIND parameter /V displays the lines where the target does not
exist. The /I parameter specifies a non-case sensitive search.

The same approach can be extended to find e.g. all the hidden files
on your harddisk. This is how it goes
  @echo off
  if exist myfiles.tmp del myfiles.tmp
  for %%f in (c d e) do attrib /s %%f:\*.* >> myfiles.tmp
  find "H      " < myfiles.tmp
  find "HR     " < myfiles.tmp
  del myfiles.tmp
The /S switch in ATTRIB means scanning also all the subdirectories.

I regularly make three complementary kinds of logs of all the files
I have on my harddisk. Below is a useful batch for the task. A
C:\DIRLOG directory is assumed to exist.
  @echo off
  rem Help if necessary
  if "%1"=="" goto _help
  ::
  rem Check that the PC has an identification
  if "%pcid_%"=="" goto _nopcid
  ::
  rem Which drive will we list
  set drive_=%1
  if not exist %drive_%:\nul goto _nodrive
  ::
  rem Make a list of the files on the drive
  c:
  cd \dirlog
  echo on
  dir /w /o:en /s %drive_%:\*.* > c:\dirlog\%pcid_%%drive_%.dir
  tree %drive_%:\ > c:\dirlog\%pcid_%%drive_%.tre
  chkdsk /v %drive_%: > c:\dirlog\%pcid_%%drive_%.pth
  @echo off
  set drive_=
  goto _out
  ::
  :_nopcid
  echo Environment variable PCID_ identifying your PC is not set, exiting
  echo Set it in your autoexec.bat
  goto _out
  ::
  :_nodrive
  echo Drive %drive_%: not found
  set drive_=
  goto _out
  ::
  :_help
  echo Usage: %0 [DriveLetter]
  ::
  :_out

Actually, when I wish to have a log of all my drives, this is what I
use:
  @echo off
  if "%pcid_%"=="" goto _unknown
  ::
  echo. |date | find /v "Enter" >  c:\direc\%pcid_%$$$.dir
  echo. |time | find /v "Enter" >> c:\direc\%pcid_%$$$.dir
  ::
  echo. |date | find /v "Enter" >  c:\direc\%pcid_%_.tre
  echo. |time | find /v "Enter" >> c:\direc\%pcid_%_.tre
  ::
  echo. |date | find /v "Enter" >  c:\direc\%pcid_%_.pth
  echo. |time | find /v "Enter" >> c:\direc\%pcid_%_.pth
  ::
  @echo on
  for %%f in (c d e f g h i j k l) do dir /w /o:en /s %%f:\*.* >> c:\direc\%pcid_%$$$.dir
  for %%f in (c d e f g h i j k l) do tree %%f:\ >> c:\direc\%pcid_%_.tre
  for %%f in (c d e f g h i j k l) do dir /b /s /o:en %%f:\*.* >> c:\direc\%pcid_%$$$.pth
  @echo off
  goto _out
  ::
  :_unknown
  echo Computer not recognized by %0 batch, exiting
  ::
  :_out

-Date: 30 Sep 2001 16:08:52 +0300
-From: ts@UWasa.Fi (Timo Salmi)
-Subject: Re: Find A File

Chris \( Val \) <chrisvalNOSPAM@NOSPAMbigpond.com.au> wrote:
> It should search each directory and find the file, what I have below finds it
> but I cand work out how to change to that directory where it was found.
> REM FOR %%X IN (A:\%1 C:\%1 D:\%1) DO DIR /S/P %%X
> TYPE %1 | MORE --- > Produces error, because I'm not in the directory.

Using the G(nu)AWK Unix port this is easy. Note that the /b, not the
/p switch is used in the dir!
  @echo off
  if "%1"=="" goto _nopar
  for %%x in (a:\%1 c:\%1 d:\%1) do dir /s/b %%x>>tmp$$$.dir
  type tmp$$$.dir|gawk '{printf "@set file_=%%s\n",$1}'>tmp$$$.bat
  call tmp$$$.bat
  type %file_% | more
  goto _out
  :_nopar
  echo Usage %0 filename
  :_out
  set file_=
  for %%f in (tmp$$$.bat tmp$$$.dir) do if exist %%f del %%f
If there are several files by the same searchname, the last one is
chosen.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:55 2002
Subject: Windows dosbox or not
Date: Fri, 1 Mar 2002 01:00:55
From: ts@uwasa.fi (Timo Salmi)

54. How can I tell if a batch is running in a Windows dosbox?
=============================================================

One can utilize the fact that under Windows the "windir" environment
variable is set. However, since it is in lower case, the status must
be tested in a roundabout way. I have tested this code on Windows
3.11 only.
  @echo off
  set | find "windir=" > wintmp$$.$$$
  copy wintmp$$.$$$ winbox$$.$$$ > nul
  if exist winbox$$.$$$ echo Running under Windows
  if not exist winbox$$.$$$ echo Not running under Windows
  if exist wintmp$$.$$$ del wintmp$$.$$$
  if exist winbox$$.$$$ del winbox$$.$$$
To find out the directory from which Windows was launched see Tom
Lavedas's http://www.zdnet.com/pcmag/pclabs/solution/uu1506c.htm.

-From: Gerry Kroll
-Subject: Re: Is DOS Running
-Date: 31 Jan 1998 02:48:44 GMT
-Organization: Public Works & Government Services (Gov't of Canada)

The above requires a small explanation:
   FIND from DOS versions before 6.0 did not set an ERRORLEVEL to
indicate the success or failure of the search.
   FIND, when used as shown above, produces *no* output when the
file being inspected does not contain any lines with the search
object. If the output of FIND is sent to a file and the search
object doesn't exist (the search fails), the result is a zero-length
file.
   You can't test for a zero-length file directly; what CAN be done
is to use COPY.  COPY will not copy a zero-length file (I hope they
NEVER fix that!!). Thus, if the copy of the original file exists,
the original file must have had a length other than zero. Therefore,
if the copy exists, the output of FIND consisted of at least one
line, and therefore the search object exists in the original file
being inspected through FIND.
   The whole thing is a LOT simpler if you have DOS 6.0 or higher.
Just run the FIND and test for the correct ERRORLEVEL.  Type  HELP
FIND at the DOS prompt.
   Regards,
   Gerry Kroll, PWGSC, Government of Canada

Timo's addition: Under MS-DOS 6+ the batch could be written as given
below. Note the required order of testing the errorlevels.
  @echo off
  set | find "windir=" > nul
  if errorlevel==1 goto _1
  if errorlevel==0 goto _0
  :_0
  echo Running under Windows
  goto _out
  :_1
  echo Not running under Windows
  :_out

 A2: Another method is to make a WIN.BAT file at path before the
Windows directory and set an environment variable to indicate that
Windows has been loaded. It is also a good usage to reset the prompt
to show you when you are in a dosbox.
  @echo off
  set prompt_=%prompt%
  set prompt=%prompt_%[win]
  set win_=winbox
  c:\windows\win
  set prompt=%prompt_%
  set prompt_=
  set win_=
To avoid multiple loading, you could use
  @echo off
  if not "%win_%"=="" goto _loaded
  set prompt_=%prompt%
  set prompt=%prompt_%[win]
  set win_=winbox
  c:\windows\win
  set prompt=%prompt_%
  set prompt_=
  set win_=
  goto _out
  :_loaded
  echo You have already loaded Windows and shelled to MS-DOS
  :_out

 A3: A third method is
  @echo off
  mem /c | find " win386 " > nul
  if errorlevel==0 if not errorlevel==1 echo Running under Windows 3.1x
  if errorlevel==1 if echo Not running under Windows 3.1x
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:56 2002
Subject: Disk drive ready?
Date: Fri, 1 Mar 2002 01:00:56
From: ts@uwasa.fi (Timo Salmi)

55. How can I test if there is a disk in a floppy disk drive?
=============================================================

The code is given below. It owes much to a posting on the Usenet
news by the batch programming wizard Tom Lavedas with personal
touches and commentary from yours truly.
  @echo off
  if "%1"=="" goto _help
  ctty nul
  %comspec% /f /c dir %1: | find "Directory of "
  ctty con
  if errorlevel==0 if not errorlevel==1 goto _isready
  echo Drive %1 is NOT ready
  goto _end
  ::
  :_isready
  echo Drive %1 is ready
  goto _end
  ::
  :_help
  echo Usage %0 [DriveLetter]
  ::
  :_end

Let's take a closer look at this instructive batch.
 1) "@echo off" turns off echoing on the subsequent lines. The @
    prevents echoing of the command itself.
 2) If no parameter was given "if "%1"=="" goto _help" presents the
    required syntax.
 3) "ctty nul" alters the default input device from the default
     con(sole) to nothing preventing any user input.
 4) "%comspec% /f /c dir %1:" call the directory command via the
    command interpreter. The undocumented /f switch avoids the
    "Abort, Retry, Fail?" input requirement if the disk is not
    present. The /c switch specifies that the command interpreter is
    to exit immediately after performing the task. Else there
    multiple shelling would pile up that would need a corresponding
    exit. The "%comspec%" environment variable specifies where the
    COMMAND.COM file is located. The "%comspec%" environment
    variable is set by a shell= line in the CONFIG.SYS file.
 5) | find "Directory of " pipes the result of "dir" to the find
    command. If a disk is present in the drive the string will be
    found and "find" returns an errorlevel of 0. Else it will return
    a higher errorlevel value.
 6) "ctty con" turns input back to the console i.e. the keyboard.
 7) "if errorlevel==0 if not errorlevel==1 goto _isready" test
    whether the value of the errorlevel is zero. If it is then the
    batch jumps to the label ":_isready". Note that logically the
    part "if errorlevel==0" is superfluous. It is there how a
    specific errorlevel value (zero) can be tested for. For more on
    testing the errorlevel see LEVEL.BAT.
 8) Most of the rest of the batch is obvious and does not need extra
    comments. In "echo Usage %0 [DriveLetter]" the %0 returns with
    path the name of the batch being run.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:57 2002
Subject: Exploiting others
Date: Fri, 1 Mar 2002 01:00:57
From: ts@uwasa.fi (Timo Salmi)

56. Could you please solve this problem for me with a batch?
============================================================

First please read http:/www.uwasa.fi/~ts/garbinfo/garb3020.html
   There is a fine line between asking for advice and trying to
exploit others with free consultation requests. It is not uncommon
to see Usenet news postings, or even email, where a user asks to
have his/her particular problem programmed for him/her. I do not
consider this fair nor quite proper. The purpose of FAQs like the
current one is to provide the readers with batch usage information
and useful tools, not to do their work on their behalf for free.
   Requests for homework assignment solutions are another phenomenon
which regularly occur on some programming newsgroups on the Usenet
news. It is not conductive to learning to help students to cheat on
their homework assignments. Here is what you, a student, should do.
First study. Start the assignment yourself. Try your very best to
solve your assignment. Then, if you really get stuck after having
tried very very hard yourself, come back on the appropriate Usenet
newsgroup with specific questions and the code which you have
devised so far. That kind of help will usually be forthcoming. But
please do not email such questions. We are not your individual
tutors.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:58 2002
Subject: Repeat loops
Date: Fri, 1 Mar 2002 01:00:58
From: ts@uwasa.fi (Timo Salmi)

57. How can I make a loop that is repeated a preset number of times?
====================================================================

The solution below, edited only in the slightest to make the style
compatible, was posted to Usenet news by Ted Davis. Ted is a major
contributor in the newsgroup with very neat batch solutions.
  @echo off
  set count_=
  set target_=!!!!!
  :_loop
    echo This is one pass %count_%
    set count_=%count_%!
    if "%count_%"=="%target_%" goto _endloop
    goto _loop
  :_endloop
  set count_=
  set target_=
The URL http://gearbox.maem.umr.edu/~batch/intrin1.htm#bang has more
on this issue.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:00:59 2002
Subject: Memory contents
Date: Fri, 1 Mar 2002 01:00:59
From: ts@uwasa.fi (Timo Salmi)

58. How can I display the contents of the memory?
=================================================

Here is the outline
  @echo off
  echo d 0000:0000  > infile.dbg
  echo d 0000:0080 >> infile.dbg
  echo d 0000:0100 >> infile.dbg
  echo d 0000:0180 >> infile.dbg
  echo d 0000:0200 >> infile.dbg
  echo q           >> infile.dbg
  debug < infile.dbg | more
  del infile.dbg

A detail. In the PC memory addressing conventions the line
  echo d 0000:0180 >> infile.dbg
is equivalent to line
  echo d 0010:0080 >> infile.dbg
The memory addresses are in hexadecimal. E.g. the above (180H) is
384 in decimal.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:00 2002
Subject: Today into a variable
Date: Fri, 1 Mar 2002 01:01:00
From: ts@uwasa.fi (Timo Salmi)

59. How get today's date elements into environment variables?
=============================================================

This fairly difficult task has become quite a frequently asked
question. The demonstration batch below does the trick. It is first
of the many alternatives listed here. It assumes that the date is of
the format "Current date is Mon 13/04/1998", i.e. that the date
separator is a slash (but this is easy to customize). Perhaps the
most common application of getting the date into the 980413 format
is making date-dependent file names, such as e.g. TS980413.TXT . The
main trick that the batch below uses is making several passes by
calling itself in the for statements. That could be avoided, but the
batch would become very long if the for statements were written out
to avoid the need of recursion.

  @echo off

  rem Determine which pass we are making
  if "%1"=="" goto _1stpass
  if "%2"=="" goto _wdpass
  if "%3"=="" goto _daypass
  if "%4"=="" goto _mmpass

  rem Extract the year
  find "/19%4" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set yearnr_=%4
  find "/20%4" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set yearnr_=%4
  goto _end

  rem Extract the month
  :_mmpass
  find "/%3/" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set monthnr_=%3
  goto _end

  rem Extract the day
  :_daypass
  find " %2/" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set daynr_=%2
  goto _end

  rem As an extra demo also get the name of the weekday
  :_wdpass
  find "%1" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set weekday_=%1
  goto _end

  rem Put the date in a file so that find can be applied on it
  :_1stpass
  echo.|date > tmp$$$.$$$

  rem Go through all the alternatives. Note the dummy x parameters
  rem to determine which part of the date is being processed
  for %%d in (Sun Mon Tue Wed Thu Fri Sat) do call %0 %%d
  for %%d in (01 02 03 04 05 06 07 08 09 10) do call %0 x %%d
  for %%d in (11 12 13 14 15 16 17 18 19 20) do call %0 x %%d
  for %%d in (21 22 23 24 25 26 27 28 29 30 31) do call %0 x %%d
  for %%d in (01 02 03 04 05 06 07 08 09 10 11 12) do call %0 x x %%d
  for %%d in (97 98 99 00 01 02) do call %0 x x x %%d

  rem Show the results
  echo %weekday_%
  echo %yearnr_%%monthnr_%%daynr_%

  rem Clean up
  set weekday_=
  set daynr_=
  set monthnr_=
  set yearnr_=
  del tmp$$$.$$$
  :_end

As usual, there are several solutions to the same problem. One of
the alternatives is to use a QBASIC approach to set the desired
environment variables. The drawback with this solution is that the
weekday is not readily available should one wish also obtain that.
  @echo off
  ::
  :: Use a QBASIC program to create a batch to set the variables
  >  tmp$$$.bas echo LET a$ = DATE$
  >> tmp$$$.bas echo LET b$ = TIME$
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "set mm_=" ; LTRIM$(MID$(a$,1,2))
  >> tmp$$$.bas echo PRINT #1, "set dd_=" ; LTRIM$(MID$(a$,4,2))
  >> tmp$$$.bas echo PRINT #1, "set yy_=" ; LTRIM$(MID$(a$,7,4))
  >> tmp$$$.bas echo PRINT #1, "set hr_=" ; LTRIM$(MID$(b$,1,2))
  >> tmp$$$.bas echo PRINT #1, "set mn_=" ; LTRIM$(MID$(b$,4,2))
  >> tmp$$$.bas echo PRINT #1, "set ss_=" ; LTRIM$(MID$(b$,7,2))
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Test that it worked
  echo Month = %mm_%
  echo Day = %dd_%
  echo Year = %yy_%
  echo Hour = %hr_%
  echo Minutes = %mn_%
  echo Seconds = %ss_%
  ::
  :: Clean up
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  set mm_=
  set dd_=
  set yy_=
  set hr_=
  set mn_=
  set ss_=

There are even other solutions that have been presented on the net
including an approach utilizing MS-DOS DEBUG and an approach
utilizing a separate MS-DOS AWK port or G(nu)AWK from Unix. Below is
my awk-based version. Probably it be done more stylishly and
efficiently, but this is an MS-DOS batches, not and awk tips
collection.
  @echo off
  ::
  :: I have to add F:\FTOOLS, where my GAWK is, temporarily to the path
  set path_=%path%
  path=%path_%;f:\ftools
  ::
  rem Assume date format dd/mm/yyyy e.g. 10/02/2001
  echo.|date|gawk 'NR==1 { printf "%%s\n", $5 }' > date$$$.tmp
  type date$$$.tmp|gawk -F/ '{ printf "set yy_=%%s\n", substr($3,3,2) }' > tmp$$$.bat
  type date$$$.tmp|gawk -F/ '{ printf "set mm_=%%s\n", $2 }' >> tmp$$$.bat
  type date$$$.tmp|gawk -F/ '{ printf "set dd_=%%s\n", $1 }' >> tmp$$$.bat
  ::
  rem Assume time format 19:33:39,37
  echo.|time|gawk 'NR==1 { printf "%%s\n", $4 }' > time$$$.tmp
  type time$$$.tmp|gawk -F: '{ printf "set hr_=%%s\n", $1 }' >> tmp$$$.bat
  type time$$$.tmp|gawk -F: '{ printf "set mn_=%%s\n", $2 }' >> tmp$$$.bat
  type time$$$.tmp|gawk -F: '{ printf "set ss_=%%s\n", substr($3,1,2) }' >> tmp$$$.bat
  ::
  :: Call the batch that has been built
  call tmp$$$.bat
  ::
  :: Show the results
  echo yy_=%yy_%
  echo mm_=%mm_%
  echo dd_=%dd_%
  echo hr_=%hr_%
  echo mn_=%mn_%
  echo ss_=%ss_%
  ::
  :: Clean up
  for %%v in (yy_ mm_ dd_ hr_ mn_ ss_) do set %%v=
  for %%f in (tmp$$$.bat date$$$.tmp time$$$.tmp) do if exist %%f del %%f
  path=%path_%
  set path_=

There is yet another, rather clear-cut (if you pardon the feeble
pun) method for getting the separate elements of date and time
information into environment variables. One of the top FAQs if not
the top batch FAQ on the Usenet news. This one uses two third-party
programs, echon (echoing without the linefeed <CR><LF>) given in
item #99 and CUT.EXE (Extract or delete text columns). There is a
compiled ECHON.EXE in this tsbat package.
  @echo off
  ::
  rem Assume date format dd/mm/yyyy e.g. 14/10/2001
  echo.|date|find "Current ">tmp$$$.txt
  echo @echo off>tmp$$$.bat
  echon set dd_=>>tmp$$$.bat
  type tmp$$$.txt|cut 21 22>>tmp$$$.bat
  echon set mm_=>>tmp$$$.bat
  type tmp$$$.txt|cut 24 25>>tmp$$$.bat
  echon set yy_=>>tmp$$$.bat
  type tmp$$$.txt|cut 29 30>>tmp$$$.bat
  ::
  rem Assume time format 10:16:54,37
  echo.|time|find "Current ">tmp$$$.txt
  echon set hr_=>>tmp$$$.bat
  type tmp$$$.txt|cut 18 19>>tmp$$$.bat
  echon set mn_=>>tmp$$$.bat
  type tmp$$$.txt|cut 21 22>>tmp$$$.bat
  echon set ss_=>>tmp$$$.bat
  type tmp$$$.txt|cut 24 25>>tmp$$$.bat
  ::
  rem Call the batch that has been built
  call tmp$$$.bat
  ::
  rem Show the results
  echo yy_=%yy_%
  echo mm_=%mm_%
  echo dd_=%dd_%
  echo hr_=%hr_%
  echo mn_=%mn_%
  echo ss_=%ss_%
  ::
  :: Clean up
  for %%v in (yy_ mm_ dd_ hr_ mn_ ss_) do set %%v=
  for %%f in (tmp$$$.txt tmp$$$.bat) do if exist %%f del %%f

If you want to remove the potential leading blanks from the hour
variable, apply
  for %%h in (0 1 2 3 4 5 6 7 8 9) do if %hr_%.==%%h. set hr_=%%h

Consider the following problem to demonstrate date extraction and
usage. A user wishes temporarily to set the date to be in the last
century to run a program that fails for the 2000's. E.g. set the
date temporarily at 08-Feb-1999. But before the solution, a few
words of warning. Changing the date will mean that if any files are
written during the operation, their datestamps will be incorrect.
This can occur if the program to be run writes anything. It can also
occur in the case some other program writes anything during the
session. Finally, if this is to foil a shareware program's time
limit, the proper thing to do is to duly register the program!
  @echo off
  ::
  :: Get the date into an environment variable
  :: Assume the following order: dd/mm/yyyy e.g. 08/02/2002
  echo.|date|gawk 'NR==1{printf"@set date_=%%s-%%s-%%s\n",substr($5,1,2),substr($5,4,2),substr($5,7,4)}'>tmp$$$.bat
  call tmp$$$.bat
  ::
  :: Set the temporary date
  echo 08-02-1999|date
  ::
  :: Run what you want to run under the temporary date
  whatever.exe
  ::
  :: Restore the original date
  echo %date_%|date
  ::
  :: Clean up
  set date_=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
The gawk line, demonstrating getting the constituents, can be
replaced by the simpler
  echo.|date|gawk 'NR==1{printf"@set date_=%%s\n",$5}'>tmp$$$.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:01 2002
Subject: Copying today's updates
Date: Fri, 1 Mar 2002 01:01:01
From: ts@uwasa.fi (Timo Salmi)

60. How can I find and copy the files updated today in a directory?
===================================================================

   The solution with batch commands only is given below. Specialized
programs, however, are more convenient and flexible. For example
ftp://garbo.uwasa.fi/pc/filecopy/pcopy93d.zip. A Y2K note.
Patriquin's pcopy93d.zip its date selection copy option
unfortunately ceased working correctly at the turn of the
millennium. Another option of a third party program would be
 81848 Nov 1 1995 ftp://garbo.uwasa.fi/pc/goldies/ccopy107.zip
 ccopy107.zip Controlled Copy file copier/mover, T.Toikkanen

On to the batch-only solution:
  @echo off
  ::
  rem Put the current date into an environment variable "datenow_"
  echo.| date | find "Current" > tmp$$$.bat
  echo set datenow_=%%4> current.bat
  call tmp$$$
  ::
  rem Clean up a bit
  del current.bat
  del tmp$$$.bat
  ::
  rem Copy all the files in a directory updated today to a floppy
  ::
  rem Clean up some more
  xcopy *.* a:\ /-y /p /d:%datenow_%
  set datenow_=
The XCOPY switch /-Y means that your permission is asked before
overwriting any file. The switch /P ask before copying any file. The
/D: switch tell that files made at %datenow_% (or later) are to be
copied.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:02 2002
Subject: Checking the status of a TSR
Date: Fri, 1 Mar 2002 01:01:02
From: ts@uwasa.fi (Timo Salmi)

61. How can I test in a batch whether a TSR program has been loaded?
===================================================================

Here is an example that does that. The example tests if the SMARTDRV
is resident in memory, but it could be any TSR.
  @echo off
  mem /c | find "SMARTDRV" > nul
  if errorlevel==2 goto _nofind
  if errorlevel==1 goto _nofind
  echo SMARTDRV is loaded
  goto _end
  :_nofind
  echo SMARTDRV is not loaded
  :_end
Another similar, even better way of doing the same thing is
  @echo off
  mem /m smartdrv | find "is using the following memory" > nul
  if errorlevel==2 goto _nofind
  if errorlevel==1 goto _nofind
  echo SMARTDRV is loaded
  goto _end
  :_nofind
  echo SMARTDRV is not loaded
  :_end

Apropos the smartdrv disk cache. If you have it loaded in your
AUTOEXEC.BAT e.g. as
  loadhigh c:\dos\smartdrv.exe /x 2048 128
it is prudent to end your batches with
  smartdrv /c
if your batch does any writing to a file or files. For more on
smartdrv see HELP SMARTDRV.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:03 2002
Subject: Getting the current drive
Date: Fri, 1 Mar 2002 01:01:03
From: ts@uwasa.fi (Timo Salmi)

62. Putting the current drive letter into an environment variable.
==================================================================

The trick is the same as in the item #18 including the trick how to
put the current directory into an environment variable.
  @echo off
  echo @echo off> volume.bat
  echo set getdrv_=%%3>> volume.bat
  dir | find "Volume"> go.bat
  call go
  if exist volume.bat del volume.bat
  if exist go.bat del go.bat
  ::
  rem show that we got it
  echo %getdrv_%
If you want to get the volume label instead of the current drive
just use %%5 instead of %%3 in the above. The batch would be
  @echo off
  echo @echo off> volume.bat
  echo set getvol_=%%5>> volume.bat
  vol | find "Volume"> go.bat
  call go
  if exist volume.bat del volume.bat
  if exist go.bat del go.bat
  ::
  rem show that we got it
  echo %getvol_%
The essence of this trick is that the "vol" outputs e.g.
            Volume in drive C is WAVE_C
and WAVE_C is the fifth parameter for Volume

Another option is given below.
  @echo off
  cd|choice /N /C:ABCDEFGHIJKLMNOPQRSTUVWXYZ @set getdrv_=>tmp$$$.bat
  call tmp$$$.bat
  echo The current drive is %getdrv_%
  if exist tmp$$$.bat del tmp$$$.bat
  set getdrv_=
This solution is based on the trick that the MS-DOS CHOICE.EXE
command will display the first letter of the input piped to it.

A similar, G(nu)AWK based solution would be
  @echo off
  cd|gawk -F: '{printf "@set getdrv_=%%s\n", $1}'>tmp$$$.bat
  call tmp$$$.bat
  echo The current drive is %getdrv_%
  if exist tmp$$$.bat del tmp$$$.bat
  set getdrv_=

A QBASIC solution alternative to this task is more lengthy:
  @echo off
  ::
  :: Ensure a .bat extension when using this batch
  echo %0 | find /i ".bat" > nul
  if errorlevel==0 if not errorlevel==1 set batname=%0
  if errorlevel==1 set batname=%0.bat
  ::
  :: Put the information of the current directory into a file
  cd>tmp$$$.dat
  ::
  :: Build QBASIC program to a file tmp$$$.bas
  set skip=
  find "'Q%skip%B" <%batname% >tmp$$$.bas
  goto _jump
  ::
  OPEN "tmp$$$.dat" FOR INPUT AS #1 'QB
  LINE INPUT #1, a$ 'QB
  CLOSE #1 'QB
  OPEN "tmp$$$.bat" FOR OUTPUT AS #1 'QB
  PRINT #1, "@set getdrv_=" ; MID$(a$, 1, 1) 'QB
  CLOSE #1 'QB
  SYSTEM 'QB
  ::
  :: Run the QBASIC program and the batch it wrote
  :_jump
  qbasic /run tmp$$$.bas
  call tmp$$$.bat
  ::
  :: Show the results
  echo The current drive letter is %getdrv_%
  goto _out
  ::
  :: Clean up
  :_out
  for %%f in (bas bat dat) do if exist tmp$$$.%%f del tmp$$$.%%f
  for %%v in (batname getdrv_) do set %%v=

Using the auxiliary programs echon and cut
  @echo off
  ::
  :: Get the information
  echon @set drive_=>tmp$$$.bat
  cd|cut 1 1>>tmp$$$.bat
  ::
  :: Call the batch that has been built
  call tmp$$$.bat
  ::
  :: Show the results
  echo drive_=%drive_%
  ::
  :: Clean up
  for %%v in (drive_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

As for getting the volume label with echon.exe and cutw.exe (the
latter in the same package as cut.exe):
  @echo off
  ::
  :: Get the information
  echon @set vol_=>tmp$$$.bat
  vol|find "Volume"|cutw 6 6>>tmp$$$.bat
  ::
  :: Call the batch that has been built
  call tmp$$$.bat
  ::
  :: Show the results
  echo vol_=%vol_%
  ::
  :: Clean up
  for %%v in (vol_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:04 2002
Subject: Pulling the drive from path
Date: Fri, 1 Mar 2002 01:01:04
From: ts@uwasa.fi (Timo Salmi)

63. How can I extract the drive letter from a full file path?
=============================================================

Here goes, assuming a full path has been given! Does not work on
write protected drives.
  @echo off
  if "%1"=="" goto _usage
  ::
  rem Get the current drive letter
  echo @echo off> volume.bat
  echo set origdrv_=%%3>> volume.bat
  dir | find "Volume"> go.bat
  call go
  if exist volume.bat del volume.bat
  if exist go.bat del go.bat
  ::
  rem Change to the drive where the program is (trick due to Tom Lavedas)
  rem The \ makes to change the drive instead of running the program
  %1\
  ::
  rem Get the drive letter after the change
  @echo off
  echo @echo off> volume.bat
  echo set getdrv_=%%3>> volume.bat
  dir | find "Volume"> go.bat
  call go
  if exist volume.bat del volume.bat
  if exist go.bat del go.bat
  ::
  rem Go back to the original drive
  %origdrv_%:
  set origdrv_=
  ::
  rem Show the result
  echo %getdrv_%
  goto _end
  ::
  :_usage
  echo Usage: %0 [FullPath, e.g. C:\DOS\ANSI.SYS]
  :_end

An alternative solution utilizing QBASIC. Note how this solution
could easily also be used for extracting any part of a string
variable by simply adjusting the parameters of LTRIM$(MID$(a$,1,1)).
  @echo off
  ::
  if "%1"=="" goto _usage
  ::
  :: A QBASIC program to create a batch for the drive variable
  >  tmp$$$.bas echo LET a$ = "%1"
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "set getdrv_=" ; LTRIM$(MID$(a$,1,1))
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  rem Show the result
  echo %getdrv_%
  ::
  :: Clean up
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  set getdrv_=
  goto _end
  ::
  :_usage
  echo Usage: %0 [FullPath, e.g. C:\DOS\ANSI.SYS]
  :_end
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:05 2002
Subject: Pausing for a moment
Date: Fri, 1 Mar 2002 01:01:05
From: ts@uwasa.fi (Timo Salmi)

64. How can I write a "SLEEP" command to pause for a certain time?
==================================================================

If the delay you want is no more than 99 seconds the answer is
fairly simple. All you need is the CHOICE command with appropriate
options. For example the following batch pauses for ten seconds. You
can, if you wish, break the wait by pressing the key b.
  @echo off
  choice /cb /t:b,10 /n > nul
  rem      ^    ^ "use b as the a break the wait key"
For longer waits a loop is needed. The following batch sets a ten
minute wait. Remove the line "echo %count_%" if you do not wish any
progress report output.
  @echo off
  set count_=.
  set target_=...........
  :_loop
  echo %count_%
  choice /cb /t:b,60 /n > nul
  set count_=.%count_%
  if not "%count_%"=="%target_%" goto _loop
  :_end
As so many items, parts of this one owe to the insights of Tom
Lavedas, and parts are totally my own (un)doing.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:06 2002
Subject: Comments in batches
Date: Fri, 1 Mar 2002 01:01:06
From: ts@uwasa.fi (Timo Salmi)

65. How can put comments into a batch file?
===========================================

There are three major options, rem, ::, and %%. Rem is the best
known, %% the least. The advantage with %% is that in can be used as
a trailing comment on a working batch line. The example below
illustrates. Furthermore, a rem can be used to create an empty file.
  @echo off             %Comment: Turn off the echo%
  rem > empty.tmp       %Comment: Create on empty file%
  dir empty.tmp
  ::
  :: Comment: Test if the drive can be written to
  if exist empty.tmp echo drive is writable
  rem
  rem Comment: Delete the empty test file
  if exist empty.tmp del empty.tmp
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:07 2002
Subject: Echoing the word off
Date: Fri, 1 Mar 2002 01:01:07
From: ts@uwasa.fi (Timo Salmi)

66. How can I echo just the word "off" in a batch file?
=======================================================

One can't use "echo off" since that is a command for turning off the
echo. The seldom, if ever, needed curiosity, taken from "MicroSoft's
Undocumented Features, Volume 1  Number 7" is this
  @echo off
  echo.off
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:08 2002
Subject: Manipulating file names
Date: Fri, 1 Mar 2002 01:01:08
From: ts@uwasa.fi (Timo Salmi)

67. How can I extract the first two characters of a file name?
==============================================================

The batch below shows how to do the task. It assumes that you have
QBASIC available. QBASIC.EXE comes with MS-DOS, at least with
version 6.22. If you have Windows95 first get it and the related
.HLP files from the system's CD-ROM. I do not use Windows95 myself,
but I believe that these files are located at \OTHER\OLDMSDOS.
   The ideas utilized below can easily be applied to other string
manipulation tasks as well.
  @echo off
  ::
  :: If no parameter is given to the batch, show its proper usage
  if "%1"=="" goto _help
  ::
  :: Prepare a QBASIC program to extract the first two characters
  :: Handle different file name types like NAME.DAT NAME and .DAT
  echo LET a$="%1" > tmp$$$.bas
  echo LET p = INSTR(1, a$, ".") - 1 >> tmp$$$.bas
  echo IF p = 0 THEN LET a$ = "" >> tmp$$$.bas
  echo IF NOT (p=-1) AND NOT (p=0) THEN LET a$ = MID$(a$, 1, p) >> tmp$$$.bas
  echo LET a$ = MID$(a$, 1, 2) >> tmp$$$.bas
  echo OPEN "tmp###.bat" FOR OUTPUT AS #1 >> tmp$$$.bas
  echo PRINT #1, "@echo off" >> tmp$$$.bas
  echo PRINT #1, "set first2_=" + a$ >> tmp$$$.bas
  echo SYSTEM >> tmp$$$.bas
  qbasic /run tmp$$$.bas
  ::
  :: Call the batch created by the QBASIC program
  call tmp###.bat
  ::
  :: Demonstrate that the result is in the first2_ environment variable
  echo The first two characters of the file name %1 are %first2_%
  goto _end
  ::
  :_help
  echo Usage %0 [NameToBeTruncated]
  ::
  :: Cleanup
  :_end
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  set first2_=

The above is not the only alternative. One can do this particular
task also without QBASIC with the trick also used in explaining
"getting the file name extension". Ralf Buschmann kindly posted the
following, instructive solution. Ralf's posting is somewhat edited
below. It is slightly less general than the QBASIC solution, since
it does not test where the basename ends.
   "Here is a different approach, that does not need QBASIC. It uses
an undocumented feature of the FOR command in this versions of DOS.
The command
  for %%d in (/filename.ext) do echo %%d
will result in
  echo f
  echo ilename.ext
The "/" character causes to "cut off" the first char of the string
in MS-DOS 6.22 (but not in MS-DOS 7).
  @echo off
  if "%1"=="" goto error
  if "%2"=="" goto error
  set chars=
  if exist count.$$$ del count.$$$ >nul
  set string=%1
  ::
  :loop
  for %%d in (/%string%) do set word=%%d
  for %%d in (/%string%) do if %%d%word%==%string% set chars=%chars%%%d
  set string=%word%
  echo x>>count.$$$
  find /c "x" count.$$$|find "%2" >nul
  if errorlevel 1 goto loop
  ::
  set word=
  set string=
  del count.$$$
  echo The first %2 characters of "%1" are "%chars%".
  goto out
  ::
  :error
  echo Usage: GETCHARS [String] [NumberOfChars]
  ::
  :out

Third party utilities can easily solve some of these batch problems.
In particular Unix command ports can be handy. A G(nu)AWK based
solution would be
  @echo off
  set name_=filename.ext
  echo %name_%|gawk '{printf "set name2_=%%s\n",substr($1,1,2)}'>tmp$$$.bat
  call tmp$$$.bat
  echo %name2_%
  if exist tmp$$$.bat del tmp$$$.bat
  set name2_=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:09 2002
Subject: Comparing two values
Date: Fri, 1 Mar 2002 01:01:09
From: ts@uwasa.fi (Timo Salmi)

68. How can I compare two numbers with batch commands?
======================================================

   There are a number of batch tasks which are readily possible by
using QBASIC. QBASIC.EXE comes with MS-DOS, at least with version
6.22. If you have Windows95 first get it and the related .HLP files
from the system's CD-ROM. I do not use Windows95 myself, but I
believe that these files are located at \OTHER\OLDMSDOS. Using
QBASIC as an aid in batch files opens a Pandora's box of
possibilities.
   One hitch in using QBASIC in batch files is that ">" and "<" are
not available, since their nature as batch redirection symbols
overrides their role as comparison operators. The batch for the
comparison of two numbers given below demonstrates one case of
needing to circumvent the redirection / comparison symbol dilemma.
   The result of comparing the two numbers is coded as follows. If
the first is smaller, equal, greater than the second the environment
variable gt_ is returned as -1, 0, and 1, respectively. (As some
programmers will recognize, this resembles the early FORTRAN
convention.)
  @echo off
  if "%2"=="" goto _help
  ::
  echo IF INSTR(STR$(%1 - %2), "-") = 0 THEN > tmp$$$.bas
  echo   LET gt$ = "1" >> tmp$$$.bas
  echo ELSE >> tmp$$$.bas
  echo   LET gt$ = "-1" >> tmp$$$.bas
  echo ENDIF >> tmp$$$.bas
  echo IF %1 = %2 THEN gt$ = "0" >> tmp$$$.bas
  echo OPEN "tmp###.bat" FOR OUTPUT AS #1 >> tmp$$$.bas
  echo PRINT #1, "@echo off" >> tmp$$$.bas
  echo PRINT #1, "set gt_=" + gt$ >> tmp$$$.bas
  echo SYSTEM >> tmp$$$.bas
  qbasic /run tmp$$$.bas
  call tmp###.bat
  echo %gt_%
  goto _end
  ::
  :_help
  echo Usage %0 [FirstNumberToBeCompared] [SecondNumberToBeCompared]
  ::
  :_end
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  set gt_=

Operations which do not initially require a ">" are more
straightforward. Below, as an example, is a further example batch to
do simple arithmetic operations utilizing QBASIC.
  @echo off
  ::
  :: Check that the syntax is right
  if "%3"=="" goto _help
  set ok_=false
  if "%2"=="+" set ok_=true
  if "%2"=="-" set ok_=true
  if "%2"=="*" set ok_=true
  if "%2"=="/" set ok_=true
  if "%2"=="^" set ok_=true
  if "%2"=="\" set ok_=true
  if "%2"=="mod" set ok_=true
  if not "%ok_%"=="true" goto _error
  ::
  :: Error message to be used should the QBASIC program fail
  echo @echo Illegal operation! The QBASIC program failed.> tmp###.bat
  ::
  :: The calculations using QBASIC
  echo ON ERROR GOTO Endit > tmp$$$.bas
  echo LET x$ = LTRIM$(STR$ (%1 %2 %3)) >> tmp$$$.bas
  echo OPEN "tmp###.bat" FOR OUTPUT AS #1 >> tmp$$$.bas
  echo PRINT #1, "@echo off" >> tmp$$$.bas
  echo PRINT #1, "set x_=" + x$ >> tmp$$$.bas
  echo Endit: >> tmp$$$.bas
  echo SYSTEM >> tmp$$$.bas
  qbasic /run tmp$$$.bas
  call tmp###.bat
  ::
  :: Show the result
  if not "%x_%"=="" echo %x_%
  goto _out
  ::
  :: Errors and help
  :_error
  echo Unknown arithmetic operation %2
  :_help
  echo Usage: %0 FirstOperand ArithmeticOperation SecondOperand
  ::
  :: Cleaning
  :_out
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  set ok_=
  set x_=

Here is another, very similar solution for the comparison. It uses
the SGN function.
  @echo off
  ::
  if "%2"=="" goto _help
  ::
  >  tmp$$$.bas echo LET a = %1
  >> tmp$$$.bas echo LET b = %2
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo IF SGN(a - b) = 1 THEN
  >> tmp$$$.bas echo   PRINT #1, "set sgn=1"
  >> tmp$$$.bas echo ELSEIF SGN(a - b) = -1 THEN
  >> tmp$$$.bas echo   PRINT #1, "set sgn=-1"
  >> tmp$$$.bas echo ELSE
  >> tmp$$$.bas echo   PRINT #1, "set sgn=0"
  >> tmp$$$.bas echo END IF
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  qbasic /run tmp$$$.bas
  call tmp###.bat
  ::
  if "%sgn%"=="1"  echo The first parameter is bigger than the second
  if "%sgn%"=="-1" echo The first parameter is smaller than the second
  if "%sgn%"=="0"  echo The parameters are equal
  ::
  del tmp$$$.bas
  del tmp###.bat
  goto _end
  ::
  :_help
  echo Usage: %0 FirstNumber SecondNumber
  :_end

Using FN.EXE
  @echo off
  if "%2"=="" goto _help
  if "%1"=="%2" set fn_=0
  if "%fn_%"=="" fn /e int((%1-%2)/abs(%1-%2)) 0>nul
  if "%fn_%"=="1"  echo The first parameter is bigger than the second
  if "%fn_%"=="-1" echo The first parameter is smaller than the second
  if "%fn_%"=="0"  echo The parameters are equal
  if "%fn_%"==""   echo A mathematical error in the operation
  set fn_=
  goto _end
  :_help
  echo Usage %0 [FirstNumberToBeCompared] [SecondNumberToBeCompared]
  :_end

A GAWK based solution:
  @echo off
  ::
  if "%2"=="" goto _help
  ::
  :: Prepare a GAWK program file
  >  tmp$$$.awk echo {
  >> tmp$$$.awk echo if (%1-%2==0) sgn=0
  >> tmp$$$.awk echo else if (index(%1-%2,"-")) sgn=-1
  >> tmp$$$.awk echo else sgn=1
  >> tmp$$$.awk echo printf "@set sgn_=%%s\n",sgn
  >> tmp$$$.awk echo }
  ::
  :: Execute
  echo %1 %2|gawk -f tmp$$$.awk>tmp$$$.bat
  call tmp$$$
  ::
  :: Display the result
  if "%sgn_%"=="1"  echo The first parameter is bigger than the second
  if "%sgn_%"=="-1" echo The first parameter is smaller than the second
  if "%sgn_%"=="0"  echo The parameters are equal
  ::
  :: Clean up
  for %%f in (tmp$$$.awk tmp$$$.bat) do if exist %%f del %%f
  for %%v in (sgn_) do set %%v=
  goto _end
  :_help
  echo Usage %0 [FirstNumberToBeCompared] [SecondNumberToBeCompared]
  :_end

To demonstrate an awk function one could write the above also as
  @echo off
  ::
  if "%2"=="" goto _help
  ::
  :: Prepare a GAWK program file
  >  tmp$$$.awk echo function sgn(x1,x2)
  >> tmp$$$.awk echo {
  >> tmp$$$.awk echo if (%1-%2==0) return 0
  >> tmp$$$.awk echo else if (index(%1-%2,"-")) return -1
  >> tmp$$$.awk echo else return 1
  >> tmp$$$.awk echo }
  >> tmp$$$.awk echo {
  >> tmp$$$.awk echo printf "@set sgn_=%%s\n",sgn(%1,%2)
  >> tmp$$$.awk echo }
  ::
  :: Execute
  echo %1 %2|gawk -f tmp$$$.awk>tmp$$$.bat
  call tmp$$$
  ::
  :: Display the result
  if "%sgn_%"=="1"  echo The first parameter is bigger than the second
  if "%sgn_%"=="-1" echo The first parameter is smaller than the second
  if "%sgn_%"=="0"  echo The parameters are equal
  ::
  :: Clean up
  for %%f in (tmp$$$.awk tmp$$$.bat) do if exist %%f del %%f
  for %%v in (sgn_) do set %%v=
  goto _end
  :_help
  echo Usage %0 [FirstNumberToBeCompared] [SecondNumberToBeCompared]
  :_end

--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:10 2002
Subject: Real wo/men use 4DOS
Date: Fri, 1 Mar 2002 01:01:10
From: ts@uwasa.fi (Timo Salmi)

69. All these solutions are for wimps. Why not rather use 4DOS?
===============================================================

4DOS is a fine alternative command interpreter that can be used to
replace MS-DOS COMMAND.COM. There are many tasks that can be
performed simply and effectively with it. For example, arithmetic
calculations considered earlier in this FAQ could be easily done
with EVAL instead of using the BASIC solutions of the vanilla
MS-DOS. However, using 4DOS also poses some problems.
  1) You have to decide whether the extra features are worth the
     hassle of changing the command interpreter.
  2) You have to weigh the advantages against the inconvenience of
     the incompatibilities. Not only of batches, but some programs
     will cease working properly if you change your command
     interpreter.
There always are better, or just other solutions, which the users of
an alternative system are eager to press on others. This is why one
often can see on the Usenet operating system advocacy postings which
are almost religious in their fervor.
   Whatever the pros and cons of 4DOS vs. COMMAND.COM, this package
has been written for vanilla MS-DOS. It does not utilize any 4DOS
additional features.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:11 2002
Subject: More than 9 parameters
Date: Fri, 1 Mar 2002 01:01:11
From: ts@uwasa.fi (Timo Salmi)

70. How can I give more than the nine parameters to a batch?
============================================================

If you need to preserve the parameters for later usage in the batch,
the method is the one below. SHIFT is a batch command that shifts
(what else) left the positions of the replaceable parameters.
  @echo off
  set _1=%1
  shift
  set _2=%1
  shift
  set _3=%1
  shift
  set _4=%1
  shift
  set _5=%1
  shift
  set _6=%1
  shift
  set _7=%1
  shift
  set _8=%1
  shift
  set _9=%1
  shift
  set _10=%1
  :: Test
  echo %_1% %_2% %_3% %_4% %_5% %_6% %_7% %_8% %_9% %_10%
Beware, however, lest the command line call exceed the maximum
length of an MS-DOS line.

If you just need the paramters once, you can use a loop
  @echo off
  if "%1%"=="" goto _end
  :_loop
    echo %1
    shift
    if not "%1"=="" goto _loop
  :_end
  echo Finished
Or, alternatively
  @echo off
  :_loop
    if "%1"=="" goto _out
    echo %1
    shift
    goto _loop
  :_out
  echo Finished
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:12 2002
Subject: Update copy files
Date: Fri, 1 Mar 2002 01:01:12
From: ts@uwasa.fi (Timo Salmi)

71. How can I update copy files from one directory to another?
==============================================================

Copying more recent files is a task which is best done with a third
party utility like

 18016 Sep 29 1992 ftp://garbo.uwasa.fi/pc/pcmagutl/dirmat31.zip
 dirmat31.zip Dirmatch update for comparing & manipulating two dirs

 122494 Aug 5 1994 ftp://garbo.uwasa.fi/pc/filecopy/pcopy93d.zip
 Norm Patriquin's previously fabulous copy program, Y2K buggy!

It is, however, possible to use a batch only solution even if it is
not as crisp.
  @echo off
  ::
  :: Decide where to go
  if "%3"=="recurse" goto _subru
  if "%2"=="" goto _usage
  if not exist %1 goto _nofiles
  ::
  :: Do it one file at a time
  for %%f in (%1) do call %0 %%f %2 recurse
  goto _end
  ::
  :_subru
  dir %1 | find /v " Volume " | find /v " Directory " | find /v " bytes"
  echo %1
  replace /u /p %1 %2
  goto :_end
  ::
  :_nofiles
  echo File %1 not found
  goto :_end
  ::
  :_usage
  echo Usage: %0 [drive1:][path1]filename [drive2:][path2]
  ::
  :_end
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:13 2002
Subject: Clearing temp
Date: Fri, 1 Mar 2002 01:01:13
From: ts@uwasa.fi (Timo Salmi)

72. How can I best clear all the files from my TEMP directory?
==============================================================

First a warning. Be very careful with this solution, since DELTREE
is a very powerful command.
  @echo off
  ::
  :: Require running under vanilla MS-DOS
  set | find "windir=" > nul
  if errorlevel==1 goto _1
  if errorlevel==0 goto _0
  :_0
  echo Do not run this batch under Windows
  goto _out
  :_1
  ::
  :: Customize the next line. Be vary careful!
  set tempdir=c:\temp
  if "%tempdir%"=="" goto _nospace
  ::
  :: Check if there is anything to delete
  if not exist %tempdir%\*.* goto _nofiles
  deltree /y %tempdir%\
  goto _out
  ::
  :_nofiles
  echo No files in, or no %tempdir%
  goto _out
  ::
  :_nospace
  echo Warning! You are out of environment space
  ::
  :_out
  set tempdir=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:14 2002
Subject: Number of parameters
Date: Fri, 1 Mar 2002 01:01:14
From: ts@uwasa.fi (Timo Salmi)

73. How can I check the number of parameters given to a batch?
==============================================================

A pedestrian solution to this question is
  @echo off
  if not "%9"=="" echo at least 9 replaceable parameters
  if "%9"=="" if not "%8"=="" echo 8 replaceable parameters
  if "%8"=="" if not "%7"=="" echo 7 replaceable parameters
  if "%7"=="" if not "%6"=="" echo 6 replaceable parameters
  if "%6"=="" if not "%5"=="" echo 5 replaceable parameters
  if "%5"=="" if not "%4"=="" echo 4 replaceable parameters
  if "%4"=="" if not "%3"=="" echo 3 replaceable parameters
  if "%3"=="" if not "%2"=="" echo 2 replaceable parameters
  if "%2"=="" if not "%1"=="" echo 1 replaceable parameters
  if "%1"=="" echo no replaceable parameters
This is not, however, a very essential batch task since e.g. "shift"
can be used to access one parameter at a time until the parameters
are exhausted.
   Another solution is a batch that gives the output as a number of
characters (.)
  @echo off
  set count_=
  :_loop
    if "%1"=="" goto _endloop
    shift
    set count_=%count_%.
    goto _loop
  :_endloop
  if not "%count_%"=="" echo %count_%
  if "%count_%"=="" echo No parameters
This method has the disadvantage of losing the parameters because of
the shift.
   A rather neat solution was posted by Walter Zackery:
@echo off
>>{c}.bat for %%f in (1 2 3 4 5 6 7 8 9) do echo if not "%%%%f"=="" set c_=%%f
call {c} %1 %2 %3 %4 %5 %6 %7 %8 %9
if exist {c}.bat del {c}.bat
echo %c_% parameters entered at the command line.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:15 2002
Subject: Deleting all *.tmp files
Date: Fri, 1 Mar 2002 01:01:15
From: ts@uwasa.fi (Timo Salmi)

74. How can I locate and e.g. delete all *.TMP files on a drive?
================================================================

The locating part is not difficult with a batch, but also performing
an operation on the files found is. Therefore, I'll recommend a
third party solution for the latter part. As for the first part, you
can use the ATTRIB command as follows
  @echo off
  attrib +a c:\*.tmp /s > nul
  attrib c:\*.tmp /s > go.bat
Then you'll have a list like
  A          C:\AN\ARCHIVE.TMP
  A          C:\AN\NOTES.TMP
  A          C:\AN2\NOTES2.TMP
The next question is how to manipulate that list of files. One
non-batch solution is to use an editor to substitute the "  A" (or
whatever) with an MS-DOS command, like DEL.
   A different, stand-alone solution is to use a specified
third-party program for the purpose. The best choices are the very
useful and flexible TARGET
 73962 Jul 21 1992 ftp://garbo.uwasa.fi/pc/filefind/target15.zip
 target15.zip The McAfee File Locator and Manipulator (good)
and the more narrow-scope
 17653 Dec 13 1987 ftp://garbo.uwasa.fi/pc/pcmagvol/vol4n24.zip
 vol4n24.zip Contains SWEEP and WAITASEC
Also see the later item #76 "How to search all the *.txt files on a
drive for a word?"

Another solution (requires gawk) with extra safeguards:
  @echo off
  echo @echo off>tmp$$$.bat
  dir /s/b c:\*.tmp|gawk '{printf "del %%s /p\n",$1}'>>tmp$$$.bat
  echo tmp$$$.bat to delete all the *.TMP files on C:\ ready!
  choice /n/c:yn Do you want to run it now [Y,N]?
  if errorlevel 2 goto _out
  if errorlevel 1 call tmp$$$
  :_out
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

A related, very simple example. If you just wish to list all the
.TMP files on a drive and show how many there are, this is how to do
it
  dir /b/l/s c:\*.tmp|find /v/n ""
For multiple drives
  @echo off
  for %%d in (c d e f g h) do dir /s/b %%d:\*.tmp>>tmp$$$.dir
  find /v/n "" tmp$$$.dir
  for %%f in (tmp$$$.dir) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:16 2002
Subject: Renaming files sequentially
Date: Fri, 1 Mar 2002 01:01:16
From: ts@uwasa.fi (Timo Salmi)

75. How can I rename all files sequentially in a directory?
===========================================================

 Q: How can I rename all files in a directory into a sequential
numerical order? E.g. 001.txt, 002.txt, 003.bat, 004.ico.

 A: This is an interesting task. The solution I have used for a
similar task involves an editor, which is effective but not very
neat batch programming. For a batch only solution one alternative is
to utilize QBASIC which is part of MS-DOS. The solution below can
easily be streamlined to do the renaming automatically, but I have
erred on the side of caution. Yes, don't damage your file structure
with haphazard experimenting. First make sure that you understand
what the batch does.
   Also note that while this batch is for all the files in a
directory, it is very easy to change it to cover only one type of
files. Just change argument in the second line of the batch. (For
example you might have dir /b /l *.txt > dirlst$$.tmp)

  @echo off
  dir /b /l *.* > dirlst$$.tmp

  echo OPEN "dirlst$$.tmp" FOR INPUT AS #1  > tmp$$$.bas
  echo OPEN "sequence.bat" FOR OUTPUT AS #2 >> tmp$$$.bas
  echo PRINT #2, "@echo off" >> tmp$$$.bas
  echo PRINT #2, "rem SEQUENCE.BAT by Prof. Timo Salmi" >> tmp$$$.bas
  echo PRINT #2, "" >> tmp$$$.bas
  echo LET i = 0 >> tmp$$$.bas
  echo DO >> tmp$$$.bas
  echo  LINE INPUT #1, a$ >> tmp$$$.bas
  echo  IF NOT INSTR(a$, ".") = 0 THEN >> tmp$$$.bas
  echo   IF NOT a$ = "dirlst$$.tmp" THEN >> tmp$$$.bas
  echo    IF NOT a$ = "sequence.bat" THEN >> tmp$$$.bas
  echo     LET i = i + 1 >> tmp$$$.bas
  echo     LET i$ = LTRIM$(STR$(i)) >> tmp$$$.bas
  echo     LET z$ = STRING$(3 - LEN(i$), ASC("0")) >> tmp$$$.bas
  echo     LET e$ = MID$(a$, INSTR(a$, ".")) >> tmp$$$.bas
  echo     PRINT #2, "copy /-y "; a$; " "; z$; i$ + e$ >> tmp$$$.bas
  echo    END IF >> tmp$$$.bas
  echo   END IF >> tmp$$$.bas
  echo  END IF >> tmp$$$.bas
  echo LOOP UNTIL (EOF(1)) >> tmp$$$.bas
  echo CLOSE #1 >> tmp$$$.bas
  echo CLOSE #2 >> tmp$$$.bas
  echo SYSTEM >> tmp$$$.bas

  qbasic /run tmp$$$.bas
  del tmp$$$.bas
  del dirlst$$.tmp

  echo.
  echo Batch file SEQUENCE.BAT should be now ready for your task.
  echo.
  echo It is advisable to first look into it before running it.
  echo As added caution copying is used instead of renaming.
  echo.

A (G)AWK based solution would be much simpler:
  dir /b /l /o:n *.* > dirlst$$.tmp
  ::
  echo @echo off > sequence.bat
  type dirlst$$.tmp|gawk -F. '{printf"rem copy /-y %%s.%%s %%s.%%s\n",$1,$2,$2,i+=1}'>>sequence.bat
  dir sequence.bat
  ::
  for %%f in (dirlst$$.tmp) do if exist %%f del %%f
If you consider the batch above more closely for a moment, you'll
probably note that you can similarly prepare many kinds of
operations on the files in a directory.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:17 2002
Subject: A sweeping find
Date: Fri, 1 Mar 2002 01:01:17
From: ts@uwasa.fi (Timo Salmi)

76. How to search all the *.txt files on a drive for a word?
============================================================

The easiest alternative would be using third-party solutions such as
 73962 Jul 21 1992 ftp://garbo.uwasa.fi/pc/filefind/target15.zip
 target15.zip The McAfee File Locator and Manipulator (good)
For a batch-only solution this is not an easy task unless one fully
utilizes the tools available in MS-DOS version 6.22. The batch below
prepares a QBASIC program for the sweep. If you understand the
solution you can customize it e.g. to accept strings instead of just
words, if need be. The version below is case-insensitive. For a case
sensitive alternative omit the /I from the FIND.
  @echo off
  dir /b /s c:\*.txt > dirlst$$.tmp
  if "%1"=="" goto _help
  ::
  echo LET q$ = CHR$(34) > tmp$$$.bas
  echo LET p$ = CHR$(37) >> tmp$$$.bas
  echo LET g$ = CHR$(62) >> tmp$$$.bas
  echo OPEN "dirlst$$.tmp" FOR INPUT AS #1  >> tmp$$$.bas
  echo OPEN "sweep$$$.bat" FOR OUTPUT AS #2 >> tmp$$$.bas
  echo PRINT #2, "@echo off" >> tmp$$$.bas
  echo PRINT #2, "rem SWEEP$$$.BAT by Prof. Timo Salmi "; DATE$ >> tmp$$$.bas
  echo PRINT #2, >> tmp$$$.bas
  echo WHILE NOT EOF(1) >> tmp$$$.bas
  echo   INPUT #1, a$ >> tmp$$$.bas
  echo   PRINT #2, "FIND /I "; q$; p$; "1"; q$, a$; g$; g$ " sweep$$$.out" >> tmp$$$.bas
  echo WEND >> tmp$$$.bas
  echo CLOSE #1 >> tmp$$$.bas
  echo CLOSE #2 >> tmp$$$.bas
  echo SYSTEM >> tmp$$$.bas
  ::
  if exist sweep$$$.out del sweep$$$.out
  qbasic /run tmp$$$.bas
  del tmp$$$.bas
  del dirlst$$.tmp
  call sweep$$$ %1
  del sweep$$$.bat   %Omit, if you wish to customize the search%
  ::
  type sweep$$$.out | more
  del sweep$$$.out
  goto _end
  ::
  :_help
  echo Usage %0 [SearchWord]
  :_end

Consider one related detail of writing batches with QBASIC. Some
characters like " (ASCII 34) % (ASCII 37) and > (ASCII 62) require
special treatment. If they are needed in a PRINT you can apply the
trick used above. That is, define a variable that contains the
character needed and use that in the print statement. The most
tricky situation is if you need to use a > or a < within the QBASIC
program to compare two number. Item #68 of this FAQ shows how to
solve this.

A (GnuAWK) based solution
  @echo off
  dir /b/l/s c:\*.txt|gawk '{printf "find \"MyText\" %%s\n",$1}'>tmp$$$.bat
  call tmp$$$.bat
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
To avoid scrolling off the screen use
  @echo off
  if exist tmp$$$.rpt del tmp$$$.rpt
  dir /b/s c:\*.txt|gawk '{printf "find \"MyText\" %%s \x3E\x3E tmp$$$.rpt \n",$1}'>tmp$$$.bat
  call tmp$$$.bat
  more < tmp$$$.rpt
  for %%f in (tmp$$$.bat tmp$$$.rpt) do if exist %%f del %%f

Finally, see item #102 for the simplest method of all. (In an
extensive FAQ like this one such overlap of items is impossible to
avoid.)
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:18 2002
Subject: Multi-command lines
Date: Fri, 1 Mar 2002 01:01:18
From: ts@uwasa.fi (Timo Salmi)

77. How can I give multiple commands on one line?
=================================================

Take a look at REPEAT.BAT in my batch file FAQ / batch collection.
 Usage: REPEAT [FileName1] [FileName2] [FileName3] ...
Essentially the trick is recursion in batch programming.

Also take a look at M.BAT
 echo Usage: M [Command1] [Pars] [-] [Command2] [Pars2] [...]
Essentially the trick is utilizing shift.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:19 2002
Subject: Zipper
Date: Fri, 1 Mar 2002 01:01:19
From: ts@uwasa.fi (Timo Salmi)

78. Is there a batch to individually zip each file in a directory?
==================================================================

 Q: "Is there a way that I can use an MS-DOS batch to PKzip all the
files in a directory without knowing what the files' names are? For
example, if I had two files in a directory SUE.DOC and FRED.TXT. I
do not know the names prior to running the batch file, but I want to
end up with two files zipped as SUE.ZIP and FRED.ZIP."

 A: If one (again) resorts to running QBASIC through a batch file,
this task can be done readily enough:
  @echo off
  ::
  :: Prepare a list of the files in the directory
  dir /b *.* > dirlst$$.tmp
  ::
  :: Handle the directory file line by line
  echo OPEN "dirlst$$.tmp" FOR INPUT  AS #1  > tmp$$$.bas
  echo OPEN "zipper$$.bat" FOR OUTPUT AS #2 >> tmp$$$.bas
  echo WHILE NOT EOF(1) >> tmp$$$.bas
  echo   INPUT #1, a$ >> tmp$$$.bas
         :: Extract the basename
  echo   LET p = INSTR(1, a$, ".") - 1 >> tmp$$$.bas
         :: Case of only extension, no basename
  echo   IF p = 0 THEN LET a$ = "" >> tmp$$$.bas
         :: Other cases
  echo   IF NOT (p=-1) AND NOT (p=0) THEN LET a$ = MID$(a$, 1, p) >> tmp$$$.bas
         :: Make a batch to do the zipping
  echo   PRINT #2, "call pkzip ",a$,a$;".*" >> tmp$$$.bas
  echo WEND >> tmp$$$.bas
  echo CLOSE #1 >> tmp$$$.bas
  echo CLOSE #2 >> tmp$$$.bas
  echo SYSTEM >> tmp$$$.bas
  ::
  :: Run the prepared QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Zip the files
  call zipper$$.bat
  ::
  :: Delete the auxiliary files and superfluous zip files
  if exist dirlst$$.tmp del dirlst$$.tmp
  if exist dirlst$$.zip del dirlst$$.zip
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp$$$.zip del tmp$$$.zip
  if exist zipper$$.bat del zipper$$.bat
  if exist zipper$$.zip del zipper$$.zip
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:20 2002
Subject: Blanket string substitution
Date: Fri, 1 Mar 2002 01:01:20
From: ts@uwasa.fi (Timo Salmi)

79. A batch to substitute a string through a set of text files?
===============================================================

 Q: I need a batch to substitute "Joe" for "Fred" through all the
*.TXT text files in a directory. How do I achieve that?

 A: Sometimes it is best to give in and not insist on a pure batch
solution without third party utilities. This is one such instance.
First, consider the substitution for a single text file. Get the
Unix-like stream editor SED.EXE (see the list at the top of this
file). Here is a simple batch to do the substitution with sed on a
test file TEST1.TXT
  @echo off
  if not exist test1.txt goto _notfound
  type test1.txt | sed -e "s/Fred/Joe/g" > tmp$$$.txt
  type tmp$$$.txt
  del tmp$$$.txt
  goto _end
  :_notfound
  echo File test1.txt not found
  :_end

The above just gives the first step in the process without altering
the source file and just creating a temporary file. if you wish to
write the result back into the original file, chance the line
  type tmp$$$.txt
to
  type tmp$$$.txt > test1.txt

Next, here is how recurse all the TEST*.TXT files in the directory
and make the desired substitutions.
  @echo off
  if "%2"=="recurse" goto _recurse
  for %%f in (test*.txt) do call batfaq %%f recurse
  goto _end
  :_recurse
  type %1 | sed -e "s/Fred/Joe/g" > tmp$$$.txt
  type tmp$$$.txt > %1
  del tmp$$$.txt
  echo %1 ready
  :_end

As most Unix-like tools SED is a flexible, but not always an easy
tool to master. For example, consider the task of extracting the
base name of a file. I'll leave you to ponder the following line and
the possibilities it opens
 dir /b TEST*.TXT | sed -e "s/\.TXT//"

Below is a genuine production batch which I had to write in a great
hurry to replace a string with another in a lot of HTM files on my
PC.
  @echo off
  rem Use recursion to go through _all_ the files, not just one
  if "%2"=="recurse" goto _recurse
  rem Check that the files to be processed factually exist
  if exist m:\*.htm goto _for
  echo No HTM files found in M:\
  goto _out
  ::
  :_for
  rem The actual recursion is called here
  for %%f in (M:\*.HTM) do call %0 %%f recurse
  goto _out
  ::
  :_recurse
  rem Handle one file at a time
  @echo off
  goto _doit
  ::
  rem Just a skipped spot to hold alternative SED command formulations
  Whatever
  ::
  :_doit
  rem Use SED for replacing the string
  type %1|f:\ftools\sed "s/opas\/tiedekn.html\"\x3EFaculties/..\/ktt\/\"\x3EFaculty/">m:\$$$
  ::
  rem Confirm that you really want the file replaced
  echo.
  type m:\$$$|find /i "facult"
  rem My small clrbuf.exe batch enhancer to clear the type ahead buffer
  rem Convenient for safety, but can be omitted. Included since tsbat71
  clrbuf
  choice /n /c:yn Ok to replace %1 [Y,N]?
  if errorlevel==2 goto _out
  move /y m:\$$$ %1
  ::
  :_out
  rem Delete the auxiliary, temporary file
  for %%f in (m:\$$$) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:21 2002
Subject: Batch programming literature
Date: Fri, 1 Mar 2002 01:01:21
From: ts@uwasa.fi (Timo Salmi)

80. Where do I find a good book or WWW page on batch programming?
=================================================================

Most books on batch programming, which I have seen, are too
elementary to be really useful to the readers of this file. Hence
this list is very brief indeed.

Jamsa, Kris (1993). Concise Guide to MS-DOS Batch Files. Microsoft
  Press. (Draws heavily on DEBUG, but might be of general interest
  to you.)

Some old issues of magazines like the PC Magazine and PC Computing
contain much useful MS-DOS lore. But now they have become so
exclusively Microsoft Windows oriented (obsessed) that they have
lost all their interest and usefulness to an MS-DOS user.

For a WWW book see http://www.pressroom.com/~tglbatch/

For further batch material on WWW you may wish to check the links on
the links page http://www.uwasa.fi/~ts/http/http2.html#batch
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:22 2002
Subject: File's date and time
Date: Fri, 1 Mar 2002 01:01:22
From: ts@uwasa.fi (Timo Salmi)

81. How to read a file's date and time into environment variables?
==================================================================

Below is a batch solution utilizing QBASIC.
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  if not exist %1 goto _nofind
  ::
  :: Put the file's information into a file
  dir %1 > dir$$$.tmp
  ::
  :: Get the information from the file
  >  tmp$$$.bas echo OPEN "dir$$$.tmp" FOR INPUT AS #1
  >> tmp$$$.bas echo FOR I = 1 to 5
  >> tmp$$$.bas echo   LINE INPUT #1, a$
  >> tmp$$$.bas echo NEXT I
  >> tmp$$$.bas echo CLOSE #1
  ::
  :: Create a batch that will set the enrironment variables
  :: May require customizing depending on your configuration
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "set filedate=" ; LTRIM$(MID$(a$,28,8))
  >> tmp$$$.bas echo PRINT #1, "set filetime=" ; LTRIM$(MID$(a$,39,5))
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Clean up
  del dir$$$.tmp
  del tmp$$$.bas
  del tmp###.bat
  ::
  :: Test
  echo %filedate%
  echo %filetime%
  ::
  :: Clean up
  set filedate=
  set filetime=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 Filename
  goto _end
  :_nofind
  echo File %1 not found
  ::
  :: End
  :_end
If you understand how the above works, you'll realize that it is
very easy to also read the size of the file into an environment
variable. This is left as an exercise. (Hint: Look at the
fields given by the DIR command, or look at the FAQ item #95.)

Let's also present a version where the file's datestamp is required
in the DD-MMM-YYYY HH:MM format:
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  if not exist %1 goto _nofind
  for %%f in (%1) do if "%%f"=="%1" goto _nowilds
  echo Don't use wildcards in the file name
  goto _end
  ::
  :: Put the file's information into a file
  :_nowilds
  dir %1 > dir$$$.tmp
  ::
  :: Get the information from the file
  >  tmp$$$.bas echo OPEN "dir$$$.tmp" FOR INPUT AS #1
  >> tmp$$$.bas echo FOR I = 1 to 5
  >> tmp$$$.bas echo   LINE INPUT #1, a$
  >> tmp$$$.bas echo NEXT I
  >> tmp$$$.bas echo CLOSE #1
  ::
  :: Create a batch that will set the enrironment variables
  :: May require customizing depending on your configuration
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "set fileyear=" ; LTRIM$(MID$(a$,34,2))
  >> tmp$$$.bas echo PRINT #1, "set filemont=" ; LTRIM$(MID$(a$,31,2))
  >> tmp$$$.bas echo PRINT #1, "set fileday="  ; LTRIM$(MID$(a$,28,2))
  >> tmp$$$.bas echo PRINT #1, "set filetime=" ; LTRIM$(MID$(a$,39,5))
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Adjust the year
  set ct_=20
  for %%y in (80 81 82 83 84) do if "%%y"=="%fileyear%" set ct_=19
  for %%y in (85 86 87 88 89) do if "%%y"=="%fileyear%" set ct_=19
  for %%y in (90 91 92 93 94) do if "%%y"=="%fileyear%" set ct_=19
  for %%y in (95 96 97 98 99) do if "%%y"=="%fileyear%" set ct_=19
  set fileyear=%ct_%%fileyear%
  ::
  :: Month as letters
  if "%filemont%"=="01" set filemont=Jan
  if "%filemont%"=="02" set filemont=Feb
  if "%filemont%"=="03" set filemont=Mar
  if "%filemont%"=="04" set filemont=Apr
  if "%filemont%"=="05" set filemont=May
  if "%filemont%"=="06" set filemont=Jun
  if "%filemont%"=="07" set filemont=Jul
  if "%filemont%"=="08" set filemont=Aug
  if "%filemont%"=="09" set filemont=Sep
  if "%filemont%"=="10" set filemont=Oct
  if "%filemont%"=="11" set filemont=Nov
  if "%filemont%"=="12" set filemont=Dec
  ::
  :: Show results
  echo %1 %fileday%-%filemont%-%fileyear% %filetime%
  ::
  :: Clean up
  for %%f in (dir$$$.tmp tmp$$$.bas tmp###.bat) do del %%f
  for %%v in (fileyear filemont fileday filetime ct_) do set %%v=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 Filename
  goto _end
  :_nofind
  echo File %1 not found
  ::
  :: End
  :_end

Without QBASIC, using my echon.exe and my cut.exe the solution would
be
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  if not exist %1 goto _nofind
  for %%f in (%1) do if "%%f"=="%1" goto _nowilds
  echo Don't use wildcards in the file name
  goto _end
  ::
  :: Put the file's information into a file
  :_nowilds
  dir %1 > dir$$$.tmp
  ::
  :: Get the information about the file
  :: Assume the (country dependent) directory format
  :: BATFAQ   BAT           928 dd/mm/yy   hh:mn
  dir %1|find "/">dir$$$.tmp
  echon @set fileday=>tmp$$$.bat
  cut 28 29<dir$$$.tmp>>tmp$$$.bat
  echon @set filemont=>>tmp$$$.bat
  cut 31 32<dir$$$.tmp>>tmp$$$.bat
  echon @set fileyear=>>tmp$$$.bat
  cut 34 35<dir$$$.tmp>>tmp$$$.bat
  echon @set filehour=>>tmp$$$.bat
  cut 39 40<dir$$$.tmp>>tmp$$$.bat
  echon @set fileminu=>>tmp$$$.bat
  cut 42 43<dir$$$.tmp>>tmp$$$.bat
  ::
  :: Call the batch that has been built
  call tmp$$$.bat
  ::
  :: Adjust the year
  set ct_=20
  for %%y in (80 81 82 83 84) do if "%%y"=="%fileyear%" set ct_=19
  for %%y in (85 86 87 88 89) do if "%%y"=="%fileyear%" set ct_=19
  for %%y in (90 91 92 93 94) do if "%%y"=="%fileyear%" set ct_=19
  for %%y in (95 96 97 98 99) do if "%%y"=="%fileyear%" set ct_=19
  set fileyear=%ct_%%fileyear%
  ::
  :: Show the results
  echo %1 %fileday%-%filemont%-%fileyear% %filehour%:%fileminu%
  ::
  :: If you want to remove the potential leading blanks from the filehour
  for %%h in (0 1 2 3 4 5 6 7 8 9) do if %filehour%.==%%h. set filehour=%%h
  echo filehour=%filehour%
  ::
  :: Clean up
  :_clean
  for %%f in (dir$$$.tmp tmp$$$.bat) do if exist %%f del %%f
  for %%v in (fileyear filemont fileday filehour fileminu ct_) do set %%v=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 Filename
  goto _end
  :_nofind
  echo File %1 not found
  ::
  :: End
  :_end
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:23 2002
Subject: Beep
Date: Fri, 1 Mar 2002 01:01:23
From: ts@uwasa.fi (Timo Salmi)

82. How would I get a beep in a batch file?
===========================================

This is simple. Insert into the batch file the line
  echo Ctrl+G
How to enter depends the control character depends on the editor you
are using. In MS-DOS Edit "Insert special characters" is done
applying the Ctrl+P,Ctrl+key combination. In other words, for the
beep you would enter the Ctrl+P,Ctrl+G combination.

If you wish to play musical notes you can do that with QBASIC e.g.
as follows
  @echo off
  echo Play it again Sam
  >tmp$$$.bas echo PLAY "l8 o3 cdefgab o4 cd"
  >>tmp$$$.bas echo SYSTEM
  qbasic /run tmp$$$.bas
  del tmp$$$.bas

Another way to play tunes, if you have a Turbo Pascal compiler, is
use the code in item #151 of ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:24 2002
Subject: Y2K old BIOS fix
Date: Fri, 1 Mar 2002 01:01:24
From: ts@uwasa.fi (Timo Salmi)

83. Y2K: My old BIOS gives a wrong year at boot. How can I fix it?
==================================================================

After the turn of the millennium an old BIOS may give dates like
2094 when the PC booted. If the date is otherwise correct, read on.
If the BIOS is not rewritable, or if you do not know how to rewrite
it, you still can utilize the following fix. (Incidentally, an old
BIOS that cannot be rewritten has at least one advantage. It is
safer from viruses than the modern ones.)
  @echo off
  rem
  rem A Y2K.BAT fix by Prof. Timo Salmi Sat 22-Jan-2000
  rem Useful for some old BIOSes which lose the year at boot
  rem To be called e.g. by the autoexec.bat
  rem Requires Timo's auxiliary batch GETDTE.BAT
  rem
  if "%1"=="" goto _usage
  call getdte.bat
  date %daynr_%-%monthnr_%-%1
  set weekday_=
  set daynr_=
  set monthnr_=
  set yearnr_=
  echo.| date | find "Current"
  echo.| time | find "Current"
  goto _out
  :_usage
  echo.
  echo Usage: Y2K Year
  echo.
  :_out

  @echo off
  rem GETDTE.BAT auxiliary batch by prof. Timo Salmi
  rem to extract date information for Y2K.BAT
  ::
  rem Determine which pass we are making
  if "%1"=="" goto _1stpass
  if "%2"=="" goto _wdpass
  if "%3"=="" goto _daypass
  if "%4"=="" goto _mmpass
  ::
  rem Extract the year
  find "/19%4" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set yearnr_=%4
  find "/20%4" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set yearnr_=%4
  goto _end
  ::
  rem Extract the month
  :_mmpass
  find "/%3/" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set monthnr_=%3
  goto _end
  ::
  rem Extract the day
  :_daypass
  find " %2/" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set daynr_=%2
  goto _end
  ::
  rem As an extra demo also get the name of the weekday
  :_wdpass
  find "%1" tmp$$$.$$$ > nul
  if not errorlevel==1 if errorlevel==0 set weekday_=%1
  goto _end
  ::
  rem Put the date in a file so that find can be applied on it
  :_1stpass
  echo.|date > tmp$$$.$$$
  ::
  rem Go through all the alternatives. Note the dummy x parameters
  rem to determine which part of the date is being processed
  for %%d in (Sun Mon Tue Wed Thu Fri Sat) do call %0 %%d
  for %%d in (01 02 03 04 05 06 07 08 09 10) do call %0 x %%d
  for %%d in (11 12 13 14 15 16 17 18 19 20) do call %0 x %%d
  for %%d in (21 22 23 24 25 26 27 28 29 30 31) do call %0 x %%d
  for %%d in (01 02 03 04 05 06 07 08 09 10 11 12) do call %0 x x %%d
  for %%d in (99 00 01 02 03 04 05 06 07 08 09) do call %0 x x x %%d
  ::
  rem Clean up
  del tmp$$$.$$$
  :_end

As usual, there are several solutions to the same problem. One of
the alternatives is to use a QBASIC approach to set the day and
month number environment variables needed in the task.
  @echo off
  ::
  if "%1"=="" goto _usage
  ::
  :: A QBASIC program to create a batch for the date variables
  >  tmp$$$.bas echo LET a$ = DATE$
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "set mm_=" ; LTRIM$(MID$(a$,1,2))
  >> tmp$$$.bas echo PRINT #1, "set dd_=" ; LTRIM$(MID$(a$,4,2))
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Set and report
  date %dd_%-%mm_%-%1
  echo.| date | find "Current"
  echo.| time | find "Current"
  ::
  :: Clean up
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  set mm_=
  set dd_=
  goto _out
  ::
  :_usage
  echo.
  echo Usage: %0 Year
  echo.
  :_out
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:25 2002
Subject: Drop file name's first letter
Date: Fri, 1 Mar 2002 01:01:25
From: ts@uwasa.fi (Timo Salmi)

84. Is there a batch to drop the first letter of a file name?
=============================================================

The question is how one can rename XABC.TXT to ABC.TXT with a batch.
The demonstration solution below assumes that you have QBASIC
available. The principles used in the example can be adapted to
other, complicated file renaming tasks.
  @echo off
  ::
  :: If no parameter is given to the batch, show its proper usage
  if "%1"=="" goto _help
  ::
  :: Prepare a QBASIC program to detect the first character
  :: Handle different file name types like NAME.DAT NAME and .DAT
  echo LET n$="%1" > tmp$$$.bas
  echo LET p = INSTR(1, n$, ".") - 1 >> tmp$$$.bas
  echo IF p = 0 THEN LET b$ = "" >> tmp$$$.bas
  echo IF NOT (p=-1) AND NOT (p=0) THEN LET b$ = MID$(n$, 1, p) >> tmp$$$.bas
  echo LET b$ = MID$(n$, 1, 1) >> tmp$$$.bas
  echo LET c$ = MID$(n$, 2) >> tmp$$$.bas
  echo OPEN "tmp###.bat" FOR OUTPUT AS #1 >> tmp$$$.bas
  echo PRINT #1, "@echo off" >> tmp$$$.bas
  echo PRINT #1, "set first_=" + b$ >> tmp$$$.bas
  echo PRINT #1, "set rest_=" + c$ >> tmp$$$.bas
  echo SYSTEM >> tmp$$$.bas
  qbasic /run tmp$$$.bas
  ::
  :: Call the batch created by the QBASIC program
  call tmp###.bat
  ::
  :: Demonstrate that the result
  rem Remove the echo in the line below and you'll have your renaming
  if not "%rest_%"=="" echo ren %first_%%rest_% %rest_%
  goto _end
  ::
  :_help
  echo Usage %0 [NameToBeTruncated]
  ::
  :: Cleanup
  :_end
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  set first_=
  set rest_=

However, using an all-out batch method is not always the best
solution. Sometimes it is better to blend in a suitable text editor.
Like here we could have
  @echo off
  echo @echo off > doit.bat
  dir /b /l | find /v "doit.bat" >> doit.bat
After getting e.g.
  @echo off
  batfaq.bas
  testfngt.bas
  batfaq2.bat
  testadd.bat
  testfngt.bat
  batfaq.bat
you'd use the column handling of say
 978851 Feb 21 1997 ftp://garbo.uwasa.fi/pc/demo/tsedos25.zip
 tsedos25.zip The SemWare Editor Pro Test-Drive, good, nagware
to first duplicate the column:
  @echo off
  batfaq.bas   batfaq.bas
  testfngt.bas testfngt.bas
  batfaq2.bat  batfaq2.bat
  testadd.bat  testadd.bat
  testfngt.bat testfngt.bat
  batfaq.bat   batfaq.bat
Then the same method to remove the first letter:
  @echo off
  batfaq.bas   atfaq.bas
  testfngt.bas estfngt.bas
  batfaq2.bat  atfaq2.bat
  testadd.bat  estadd.bat
  testfngt.bat estfngt.bat
  batfaq.bat   atfaq.bat
And, finally you would use a keyboard macro to insert the ren to
complete your batch.
  @echo off
  ren batfaq.bas   atfaq.bas
  ren testfngt.bas estfngt.bas
  ren batfaq2.bat  atfaq2.bat
  ren testadd.bat  estadd.bat
  ren testfngt.bat estfngt.bat
  ren batfaq.bat   atfaq.bat

Third party utilities can easily solve some of these batch problems.
In particular Unix command ports can be handy. A G(nu)AWK based
solution would be
  @echo off
  set name_=Testing dropping the first letter
  echo %name_%|gawk '{n=length();printf"set name2_=%%s\n",substr($0,2,n)}'>tmp$$$.bat
  call tmp$$$.bat
  echo %name2_%
  set name2_=
  if exist tmp$$$.bat del tmp$$$.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:26 2002
Subject: Closing MS-DOS window
Date: Fri, 1 Mar 2002 01:01:26
From: ts@uwasa.fi (Timo Salmi)

85. How do I close the command window once my batch file has run?
=================================================================

Well this is a vanilla MD-DOS batch FAQ, but the solution is very
simple. Just put EXIT as the last line of your batch and the MS-DOS
box will close, if you are using Windows.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:27 2002
Subject: Re: Future of DOS batch files?
Date: Fri, 1 Mar 2002 01:01:27
From: ts@uwasa.fi (Timo Salmi)

86. Is MS-DOS batch programming going to die along with W2K?
============================================================

An edited extract from the Usenet news
:I've heard via the grapevine that Windows 2000 won't run DOS
:programs.
:If that's the case, then are DOS files about to disappear into
:obscurity like CPM SUBMIT files?

Good question, but why would that be the case? I've said this often
before. The _relative_ number of W2K users is likely to grow
drastically, but if history is anything to go by, the absolute
number of MS-DOS users will not go down, maybe even the contrary.
The old PC base just is too large. This view is supported by the
observation that e.g. Garbo archive's MS-DOS section downloads are
on the level or higher than ever before, despite everything that has
happened on the PC / Windows scene. No, I do not expect MS-DOS batch
programming becoming extinct for quite a while. However, there
unfortunately continues to be an increasing number of incompatible
platforms, meaning an increasing fragmentation of skills and
knowledge, thanks to Microsoft's chosen paths.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:28 2002
Subject: Re: Testing for a substring
Date: Fri, 1 Mar 2002 01:01:28
From: ts@uwasa.fi (Timo Salmi)

87. How can I test for the existence of a substring in a string?
================================================================

>Timo
>I read your article on how to identify a computer via batch [item
>#35]. I have a similar problem, but i need to identify them with
>only part of a string
>  ex.: if NOT %COMPUTERNAME%==JV-CCI* GOTO END
>  where %COMPUTERNAME% is JV-CCI-1256
>wildcards like * does not seem to work, do you have another
>solution?

The problem can be solved with an auxiliary file and MS-DOS FIND:
  @echo off
  ::
  rem Set test strings for the demonstration
  set string_=WhateverLongString
  set subst_=Long
  ::
  rem Put the string into
  echo %string_% > tmp$$$
  ::
  rem Test for the existence of the substring
  find "%subst_%" tmp$$$ > nul
  if errorlevel==2 goto _2
  if errorlevel==1 goto _1
  goto _0
  ::
  rem The different outcomes
  :_0
  echo Match found
  goto _out
  :_1
  echo Match not found
  goto _out
  :_2
  echo Match not found, and error
  goto _out
  ::
  rem Clean up
  :_out
  if exist tmp$$$ del tmp$$$
  set string_=
  set subst_=

This brings up another interesting problem. Is it possible to
pinpoint the location of a substring in a string with just the
original batch commands. A bit cumbersome, but possible, yes.
  @echo off
  if "%2"=="" goto _usage
  set i_=0
  echo %2|find "%1">nul
  if errorlevel==2 goto _result
  if errorlevel==1 goto _result
  echo %1|choice /c%2>nul
  for %%i in ( 0 1 2 3 4 5 6 7 8 9) do if errorlevel==%%i set i_=%%i
  for %%i in ( 10 11 12 13 14 15 16 17 18 19) do if errorlevel==%%i set i_=%%i
  for %%i in ( 20 21 22 23 24 25 26 27 28 29) do if errorlevel==%%i set i_=%%i
  for %%i in ( 30 31 32 33 34 35 36 37 38 39) do if errorlevel==%%i set i_=%%i
  for %%i in ( 40 41 42 43 44 45 46 47 48 49) do if errorlevel==%%i set i_=%%i
  :_result
  echo The (first) location of %1 in %2 is %i_%
  set i_=
  goto _out
  :_usage
  echo Usage: %0 SubString String
  :_out

Using G(nu)AWK the solution is:
  @echo off
  if "%2"=="" goto _usage
  echo.|gawk '{printf "set index_=%%s\n",index(%2,%1)}'>tmp$$$.bat
  call tmp$$$
  echo The (first) location of %1 in %2 is %index_%
  for %%v in (index_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  goto _out
  :_usage
  echo Usage: %0 SubString String
  :_out
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:29 2002
Subject: Re: Getting find results
Date: Fri, 1 Mar 2002 01:01:29
From: ts@uwasa.fi (Timo Salmi)

88. How can I put MS-DOS FIND results into an environment variable?
===================================================================

This neat solution was presented by Tom Lavedas for versions when
errorlevel was not yet returned. Only minor stylistic modifications
have been made. The number of occurrences of the search string is
put into the found_ environment variable.
  @echo off
  if not exist target.txt goto _nofile
  find /c "ZXZ" target.txt > tmp$#$#$.bat
  echo set found_=%%2> ----------.bat
  call tmp$#$#$.bat ----------.bat
  echo %found_%
  if exist ----------.bat del ----------.bat
  if exist tmp$#$#$.bat del tmp$#$#$.bat
  set found_=
  goto _end
  :_nofile
  echo Target file not found
  :_end
If you wish to understand this batch, see what is contained in the
files tmp$#$#$.bat and ----------.bat during the process.

To continue myself, consider the question of testing whether your
FIND version returns an errorlevel or not. This is how to do it
  @echo off
  echo test|find "level"
  if errorlevel==1 echo This FIND.EXE version returns an errorlevel
  if not errorlevel==1 echo This FIND.EXE version does not return an errorlevel

With the same notation, the find results can be also tested (whether
an errorlevel is returned or not) by using something like the
following demonstration
  @echo off
  echo test|find "test">tmp$$$.$$$
  copy tmp$$$.$$$ found$$$.$$$>nul
  if exist found$$$.$$$ echo The find status is true
  if not exist found$$$.$$$ echo The find status is false
  for %%f in (tmp$$$.$$$ found$$$.$$$) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:30 2002
Subject: Unambiguous directory dates
Date: Fri, 1 Mar 2002 01:01:30
From: ts@uwasa.fi (Timo Salmi)

89. How to get months as letters and years in four digits in DIR?
=================================================================

Too difficult without auxiliary programs so first get SED.EXE.
Then you can use the following
  @echo off
  rem Display the directory with 4-digit years and months as letters
  rem Assumes a date format dd/mm/yy requiring in CONFIG.SYS e.g.
  rem COUNTRY=032,850,C:\DOS\COUNTRY.SYS
  ::
  :: Check that the Unix-like SED.EXE program is available
  set found_=
  if exist sed.exe set found_=yes
  for %%d in (%path%) do if exist %%d\sed.exe set found_=yes
  for %%d in (%path%) do if exist %%dsed.exe set found_=yes
  if "%found_%"=="yes" goto _continue
  echo sed.exe is not in the current directory or at path
  goto _out
  ::
  :: Make the substitutions
  :_continue
  dir %1 | find /v "TMP1$$$     " > tmp2$$$
  type tmp2$$$ | sed -e "s#/00  #/2000  #" -e "s#/9#/199#" -e "s#/8#/198#" > tmp1$$$
  type tmp1$$$ | sed -e "s#/01/#-Jan-#" -e "s#/02/#-Feb-#" > tmp2$$$
  type tmp2$$$ | sed -e "s#/03/#-Mar-#" -e "s#/04/#-Apr-#" > tmp1$$$
  type tmp1$$$ | sed -e "s#/05/#-May-#" -e "s#/06/#-Jun-#" > tmp2$$$
  type tmp2$$$ | sed -e "s#/07/#-Jul-#" -e "s#/08/#-Aug-#" > tmp1$$$
  type tmp1$$$ | sed -e "s#/09/#-Sep-#" -e "s#/10/#-Oct-#" > tmp2$$$
  type tmp2$$$ | sed -e "s#/11/#-Nov-#" -e "s#/12/#-Dec-#" > tmp1$$$
  ::
  :: Display the results
  type tmp1$$$
  ::
  :: Clean up
  :_out
  if exist tmp1$$$ del tmp1$$$
  if exist tmp2$$$ del tmp2$$$
  set found_=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:31 2002
Subject: Future of MS-DOS batch files
Date: Fri, 1 Mar 2002 01:01:31
From: ts@uwasa.fi (Timo Salmi)

90. Isn't MS-DOS dead with W2K? So why bother with MS-DOS batches?
==================================================================

Good question, but why would that be the case? The _relative_ number
of Windows 2000 users is likely to grow drastically. But if history
is anything to go by, the _absolute_ number of MS-DOS users will not
go down, maybe even the contrary. The old PC base is just too large.
This is corroborated by the observation that e.g. Garbo archive's
MS-DOS section downloads are level or higher than ever before,
despite everything that has happened on the PC / Windows scene.
There are from 2000 up to 10000 MS-DOS related downloads from Garbo
MS-DOS archives each and every day. That's still far from dead even
if it is obvious that some quarters actively try to bury it.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:32 2002
Subject: Stopping a called batch file?
Date: Fri, 1 Mar 2002 01:01:32
From: ts@uwasa.fi (Timo Salmi)

91. How can I exit the called batch without returning to the first?
===================================================================

 Q: "Batch1 calls Batch2.  On certain conditions I want Batch2 to
stop and not return control to Batch1. How can this be done?"

A: You can achieve the virtual EFFECT by the following kind of usage

  @echo off
  rem This file is BATFAQ.BAT
  set exit_=
  call batfaq2
  if "%exit_%"=="yes" goto _end
  echo We are now in the first batfaq
  :_end
  set exit_=

  @echo off
  rem This file is BATFAQ2.BAT
  echo We are now in the second batfaq2
  if [WhateverYourCondition] set exit_=yes
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:33 2002
Subject: Stopping a called batch file?
Date: Fri, 1 Mar 2002 01:01:33
From: ts@uwasa.fi (Timo Salmi)

92. How do I create a sequence of directories from 001 to MaxValue?
===================================================================

QBASIC is a very powerful additional batch tool which allows you to
program many things which would be overly complicated else. Below is
a batch solution utilizing QBASIC. For the demonstration it creates
the directories at the C:\TEMP directory. Customize as appropriate.
The same method is naturally applicable for other similar sequencing
tasks, as well.
  @echo off
  if "%1"=="" goto _usage
  ::
  if not exist C:\TEMP\nul mkdir C:\TEMP
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  >  tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo FOR I = 1 to %1
  >> tmp$$$.bas echo   LET i$ = LTRIM$(STR$(i))
  >> tmp$$$.bas echo   LET i$ = STRING$(3-LEN(i$),"0") + i$
  >> tmp$$$.bas echo   PRINT #1, "mkdir C:\TEMP\";i$
  >> tmp$$$.bas echo NEXT I
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program prepared in the above
  qbasic /run tmp$$$.bas
  ::
  :: Call the tmp###.bat batch created by the tmp$$$.bas QBASIC program
  call tmp###.bat
  ::
  :: Delete the auxiliary files
  del tmp$$$.bas
  del tmp###.bat
  goto _out
  ::
  :_usage
  echo Usage: %0 MaxValue
  :_out
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:34 2002
Subject: Performing calculations
Date: Fri, 1 Mar 2002 01:01:34
From: ts@uwasa.fi (Timo Salmi)

93. How do I add +1 to a variable in a batch?
=============================================

Again a QBASIC is the easiest solution even if there exist clever,
complicated other solutions.
  @echo off
  ::
  set number1_=145
  set add_=1
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  >  tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo LET i = %number1_% + %add_%
  >> tmp$$$.bas echo PRINT #1, "set number2_=";LTRIM$(STR$(i))
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program prepared in the above
  qbasic /run tmp$$$.bas
  ::
  :: Call the tmp###.bat batch created by the tmp$$$.bas QBASIC program
  call tmp###.bat
  ::
  :: Delete the auxiliary files
  del tmp$$$.bas
  del tmp###.bat
  ::
  echo The result of: add %number1_% +%add_% = %number2_%
  set number1_=
  set number2_=
  set add_=

Utilizing my "FN.EXE FuNction evaluator"
  @echo off
  set number_=145
  echo number_=%number_%
  fn /e %number_%+1 0 > nul
  set number_=%fn_%
  echo number_=%number_%
  set number_=

Utilizing G(nu)AWK the solution would be
  @echo off
  set number1_=145
  set add_=1
  ::
  echo %number1_%
  echo %number1_%|gawk '{ printf "@set number1_=%%s\n",$1+=%add_% }'>tmp###.bat
  call tmp###.bat
  echo %number1_%
  ::
  set number1_=
  set add_=
  for %%f in (tmp###.bat) do if exist %%f del %%f
This gives an interesting method of performing incrementing loops.
Run this deminstration:
  @echo off
  set number_=0
  set add_=1
  ::
  :_next
  echo %number_%|gawk '{ printf "@set number_=%%s\n",$1+=%add_% }'>tmp###.bat
  call tmp###.bat
  echo %number_%
  if "%number_%"=="10" goto _out
  goto _next
  ::
  :_out
  set number_=
  set add_=
  for %%f in (tmp###.bat) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:35 2002
Subject: Errorlevel into a variable
Date: Fri, 1 Mar 2002 01:01:35
From: ts@uwasa.fi (Timo Salmi)

94. How to copy the errorlevel into a variable in a batch?
==========================================================

First let's recall the fundamental fact that batches do not return
errorlevels, program's do. So the question actually is "How do I
capture into a batch environment variable the errorlevel that an
executable program has returned?" Below is an example batch.
  @echo off
  echo +---------------------------------------------------+
  echo | A testbench for a program's errorlevel            |
  echo | Copyright (c) by Prof. Timo Salmi                 |
  echo | Email: ts@uwasa.fi  WWW: http://www.uwasa.fi/~ts/ |
  echo +---------------------------------------------------+
  echo.
  ::
  if "%1"=="" goto _help
  if exist %1 goto _continue
  if exist %1.com goto _continue
  if exist %1.exe goto _continue
  goto _notfound
  ::
  :_continue
  %1 %2 %3 %4 %5 %6 %7 %8 %9
  ::
  for %%i in (0 1 2 3 4 5 6 7 8 9) do if errorlevel==%%i set _el=%%i
  for %%i in (10 11 12 13 14 15 16) do if errorlevel==%%i set _el=%%i
  rem Add here all the missing, intervening rows yourself.
  for %%i in (250 251 252 253 254 255) do if errorlevel==%%i set _el=%%i
  echo Errorlevel==%_el%
  goto _out
  ::
  :_notfound
  echo File %1 not found
  goto _out
  ::
  :_help
  echo Usage ERRLEV ProgramName [Parameter1] [Parameter2] [...]
  ::
  :_out
  set _el=
For more, see the earlier item #23.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:36 2002
Subject: Zero byte file testing
Date: Fri, 1 Mar 2002 01:01:36
From: ts@uwasa.fi (Timo Salmi)

95. How can I test for a zero byte file? Or against a larger size?
==================================================================

There is more than one option to solve this. The classic method is
copying the file. A zero-length file will not copy.
  @echo off
  if "%1"=="" goto _help
  if not exist %1 goto _notfound
  copy %1 tmp$#$#$.$$$ > nul
  if not exist tmp$#$#$.$$$ echo File %1 is zero-length
  if exist tmp$#$#$.$$$ echo File %1 is not zero-length
  if exist tmp$#$#$.$$$ del tmp$#$#$.$$$
  goto :_out
  :_notfound
  echo File %1 not found
  goto :_out
  :_help
  echo Usage: %0 Filename
  :_out

A more refined method without the unnecessary overhead of the
copying is to compare the target file against the system's NUL file.
  @echo off
  if "%1"=="" goto _help
  if not exist %1 goto _notfound
  fc /b %1 nul | find "FC: no differences encountered" > nul
  if errorlevel==2 echo The batch failed
  if errorlevel==1 if not errorlevel==2 echo File %1 is not zero-length
  if errorlevel==0 if not errorlevel==1 echo File %1 is zero-length
  goto :_out
  :_notfound
  echo File %1 not found
  goto :_out
  :_help
  echo Usage: %0 Filename
  :_out

A third option is putting the file size into an environment variable
(size_) with a QBASIC program and then to test whether the size
equals zero.
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  if not exist %1 goto _nofind
  for %%f in (%1) do if "%%f"=="%1" goto _nowilds
  echo Don't use wildcards in the file name
  goto _end
  ::
  :: Put the file's information into a file
  :_nowilds
  dir %1 > dir$$$.tmp
  ::
  :: Get the information from the file
  >  tmp$$$.bas echo OPEN "dir$$$.tmp" FOR INPUT AS #1
  >> tmp$$$.bas echo FOR I = 1 to 5
  >> tmp$$$.bas echo   LINE INPUT #1, a$
  >> tmp$$$.bas echo NEXT I
  >> tmp$$$.bas echo CLOSE #1
  ::
  :: Create a batch that will set the size enrironment variable
  :: Might require customizing depending on your configuration
  :: Do away with a possible points in the size
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo LET a$ = LTRIM$(MID$(a$,13,14))
  >> tmp$$$.bas echo FOR I = 1 TO 3
  >> tmp$$$.bas echo  LET p = INSTR(1, a$, ".")
  >> tmp$$$.bas echo  IF p = 0 THEN
  >> tmp$$$.bas echo   LET a$ = LTRIM$(MID$(a$,1,14))
  >> tmp$$$.bas echo  ELSE
  >> tmp$$$.bas echo   LET a$ = LTRIM$(MID$(a$,1,p - 1)) + LTRIM$(MID$(a$,p+1,14))
  >> tmp$$$.bas echo  ENDIF
  >> tmp$$$.bas echo NEXT I
  >> tmp$$$.bas echo PRINT #1, "set size_=" ; a$
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Clean up
  for %%f in (dir$$$.tmp tmp$$$.bas tmp###.bat) do del %%f
  ::
  :: Show (test) results
  echo %1 %size_% bytes
  if "%size_%"=="0" echo It is an empty file
  if not "%size_%"=="0" echo It is not an empty file
  ::
  :: Clean up
  set size_=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 Filename
  goto _end
  :_nofind
  echo File %1 not found
  ::
  :: End
  :_end

The size of the file can alternatively be put in an environment
variable using an awk Unix port. The code below shows testing that
part only, since the logic otherwise is exactly same as in the
solution above. One possible program to use is GAWK.EXE. Note that
for wrapping reasons the second and third line must be on a single
line.
  @echo off
  dir WhatEver.Fle |   (The line continues below)
    gawk 'NR==6 { printf "set size_=%%s\n", $3 }' > tmp$$$.bat
  call tmp$$$.bat
  echo %size_%
  set size_=

An example of the alternative using ECHON.EXE and CUT.EXE
  @echo off
  echon set size_=>tmp$$$.bat
  dir WhatEver.Fle|find "/"|cut 14 26>>tmp$$$.bat
  call tmp$$$
  if %size_%.==0. echo The file is a zero-byte file
  if not %size_%.==0. echo The file is not a zero-byte file
  set size_=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

Next, consider the task of testing if a file is under 10000 bytes in
size.
  @echo off
  rem The file to be checked
  if not "%1"=="" goto _exist
  echo Usage: %0 FileName
  goto _out
  :_exist
  if exist %1 goto _getinfo
  echo File %1 not found
  goto _out
  ::
  :_getinfo
  echon set size_=>tmp$$$.bat
  dir %1|find "/"|cut 13 26|sed "s/ //g;s/\.//g">>tmp$$$.bat
  call tmp$$$
  ::
  :: Evaluate and report
  fn /e %size_%-10000 0 > nul
  echo %fn_%|find "-"
  if errorlevel==1 goto _gt
  echo File %1 size less than 10000 bytes
  goto _clean
  :_gt
  echo File %1 size at least 10000 bytes
  ::
  :: Clean up
  :_clean
  for %%v in (size_ fn_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  ::
  :_out

Next consider the task of finding all the larger than 10000 bytes
batch files on a drive. Using the third-part program
ftp://garbo.uwasa.fi/pc/filefind/target15.zip the trick is
 target -gt10000 c:\*.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:37 2002
Subject: Last N lines
Date: Fri, 1 Mar 2002 01:01:37
From: ts@uwasa.fi (Timo Salmi)

96. How to show the last N lines of a file using DOS batch files?
=================================================================

One could try a QBASIC solution in line with the other items in my
batch FAQ (See e.g. item #107 for getting the number of lines in a
file). Furthermore, innovative, but complicated batch-only solutions
have been suggested on the Usenet news. However, this is one of the
situations where a program ported from Unix to MS-DOS makes a
complicated task quite trivial. There are several TAIL programs at
Garbo program archive's directory of "Unix like MS-DOS programs and
material". The contents of the packages are listed in

 ftp://garbo.uwasa.fi/pc/unix/0unix.zip
 .zip files contents in /pc/unix directory

One of the several options is

 286909 Feb 17 1995 ftp://garbo.uwasa.fi/pc/unix/dosnx23b.zip
 dosnx23b.zip Dosnix Unix style MS-DOS utilities from G.Vrooman

You can also access the collections directly through WWW at
http://garbo.uwasa.fi/pc/unix.html

If you are not familiar with the tail program, the essentials are
its description "tail - deliver the last part of a file" and its
usage to display e.g. the last file lines of a file:
  tail -5 Filename
or
  type Filename | tail -5

If you only need the very last line of the file, and have SED, then
  sed -n $p Filename

Another solution for the task (compare with item #107) using FN, SED
and ECHON is
  @echo off
  if "%1"=="" goto _usage
  echon @set length_=>tmp$$$.bat
  find /n/v "" %1|sed -n $p|sed -e "s/\].*//" -e "s/\[//">>tmp$$$.bat
  call tmp$$$.bat
  fn /e %length_%-4 0>nul
  sed -n %fn_%,$p %1
  goto _out
  :_usage
  echo Usage: %0 [FileName]
  :_out
  for %%v in (length_ fn_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

There also are the GAWK based solutions posted by Ted Davis. To get
the last line apply
  awk "{Array[NR]=$0}END{print Array[NR]}"
To emulate "tail" the following prints the last 5 lines
  awk "{Array[NR]=$0}END{for(x=(NR-5);x!=NR+1;x++)print Array[x]}"
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:38 2002
Subject: Locating and full path
Date: Fri, 1 Mar 2002 01:01:38
From: ts@uwasa.fi (Timo Salmi)

97. How do I get the path of a file located somewhere on my disk?
=================================================================

For a chosen drive:
  dir /s /b c:\file.xxx
For the default drive:
  dir /s /b \file.xxx

So far, so good. But what if you wish to perform some operations on
the file found. Say you wish to copy the first found match to the
m:\ root directory on your disk. This is how to go about it
  @echo off
  if "%1"=="" goto _help
  dir /s /b /l %1 > tmp$#$#$.dir
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  >  tmp$$$.bas echo OPEN "tmp$#$#$.dir" FOR INPUT AS #1
  >> tmp$$$.bas echo INPUT #1,a$
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #2
  >> tmp$$$.bas echo PRINT #2, "@echo off"
  >> tmp$$$.bas echo PRINT #2, "copy "; a$; " m:\"
  >> tmp$$$.bas echo CLOSE #2
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program prepared in the above
  qbasic /run tmp$$$.bas
  ::
  :: Call the tmp###.bat batch created by the tmp$$$.bas QBASIC program
  call tmp###.bat
  ::
  :: Delete the auxiliary files
  if exist tmp$$$.bas del tmp$$$.bas
  if exist tmp###.bat del tmp###.bat
  if exist tmp$#$#$.dir del tmp$#$#$.dir
  goto :_out
  ::
  :_help
  echo Usage: %0 DriveLetter:\Filename
  ::
  :_out

Another option for finding is putting the file name into an
environment variable (which also makes it easy to perform subsequent
operation on the file) is given below
  @echo off
  if "%1"=="" goto _help
  for %%f in (%1) do if "%%f"=="%1" goto _nowilds
  echo Don't use wildcards in the file name
  goto _out
  ::
  :: Get the information
  :: ECHON.EXE from the tsbat package is needed
  :_nowilds
  >  tmp$$$2.bat echon @set fpath_=
  >> tmp$$$2.bat dir /s /b %1
  ::
  :: Take the first occurrence if there are duplicate names
  find "@set fpath_=" < tmp$$$2.bat > tmp$$$.bat
  ::
  :: Run the auxiliary batch to set the environment variable
  call tmp$$$.bat
  ::
  :: Show the results
  echo fpath_=%fpath_%
  ::
  :: Clean up
  set fpath_=
  for %%f in (tmp$$$.bat tmp$$$2.bat) do if exist %%f del %%f
  goto _out
  ::
  :_help
  echo Usage: %0 DriveLetter:\Filename
  ::
  :_out
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:39 2002
Subject: Locating and full path
Date: Fri, 1 Mar 2002 01:01:39
From: ts@uwasa.fi (Timo Salmi)

98. How to read a file's attributes into environment variables?
===============================================================

The QBASIC solution used many times throughout this FAQ can be
easily modified also for this task.
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  if not exist %1 goto _nofind
  for %%f in (%1) do if "%%f"=="%1" goto _nowilds
  echo Don't use wildcards in the file name
  goto _end
  ::
  :: Put the file's information into a file
  :_nowilds
  attrib %1 > dir$$$.tmp
  ::
  :: Get the information from the file
  >  tmp$$$.bas echo OPEN "dir$$$.tmp" FOR INPUT AS #1
  >> tmp$$$.bas echo LINE INPUT #1, a$
  >> tmp$$$.bas echo CLOSE #1
  ::
  :: Create a batch that will set the enrironment variables
  :: May require customizing depending on your configuration
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "set arch_=" ; LTRIM$(MID$(a$,3,1))
  >> tmp$$$.bas echo PRINT #1, "set syst_=" ; LTRIM$(MID$(a$,6,1))
  >> tmp$$$.bas echo PRINT #1, "set hidd_=" ; LTRIM$(MID$(a$,7,1))
  >> tmp$$$.bas echo PRINT #1, "set rdon_=" ; LTRIM$(MID$(a$,8,1))
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Clean up
  del dir$$$.tmp
  del tmp$$$.bas
  del tmp###.bat
  ::
  :: Show results
  echo %1 %arch_% %syst_% %hidd_% %rdon_%
  ::
  :: Clean up
  for %%v in (arch_ syst_ hidd_ rdon_) do set %%v=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 Filename
  goto _end
  :_nofind
  echo File %1 not found
  ::
  :: End
  :_end

Alternatively, with ECHON.EXE and CUT.EXE (and without QBASIC)
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  if not exist %1 goto _nofind
  for %%f in (%1) do if "%%f"=="%1" goto _nowilds
  echo Don't use wildcards in the file name
  goto _end
  ::
  :: Put the file's information into a file
  :_nowilds
  attrib %1 > dir$$$.tmp
  ::
  :: Get the information from the file
  :: Run the batch that sets the environment variables
  echo 123456789 123456789 123456789 123456789
  type dir$$$.tmp
  >  tmp$$$.bat echo @echo off
  >> tmp$$$.bat echon set arch_=
  >> tmp$$$.bat cut 3 3 <dir$$$.tmp
  >> tmp$$$.bat echon set syst_=
  >> tmp$$$.bat cut 6 6 <dir$$$.tmp
  >> tmp$$$.bat echon set hidd_=
  >> tmp$$$.bat cut 7 7 <dir$$$.tmp
  >> tmp$$$.bat echon set rdon_=
  >> tmp$$$.bat cut 8 8 <dir$$$.tmp
  call tmp$$$.bat
  ::
  :: Show results
  echo %1 %arch_% %syst_% %hidd_% %rdon_%
  ::
  :: Clean up
  for %%v in (arch_ syst_ hidd_ rdon_) do set %%v=
  for %%f in (dir$$$.tmp tmp$$$.bat) do if exist %%f del %%f
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 Filename
  goto _end
  :_nofind
  echo File %1 not found
  ::
  :: End
  :_end
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:40 2002
Subject: Echo -n
Date: Fri, 1 Mar 2002 01:01:40
From: ts@uwasa.fi (Timo Salmi)

99. Is it possible to echo without linefeed like the Unix echo -n?
==================================================================

Let's be unorthodox, and not attempt a strict batch only solution.
If you have a suitable compiler you can program an echon.exe command
to do the equivalent of the Unix echo -n. E.g. in Turbo Pascal all
ECHON.PAS takes is
  type CommandLineType = string[127];
  var  CommandLinePtr  : ^CommandLineType; s : string; i : byte;
  begin
    CommandLinePtr := Ptr(PrefixSeg, $80);
    s := CommandLinePtr^;
    for i := 2 to Length(s) do write (s[i]);
  end.
If you do not have a TP compiler, an ECHON.EXE is included in this
batch tips package. Furthermore, debug-based solutions have been
presented on the Usenet news

In the batch you might have e.g. something like
  @echo off
  echon Working...
  pkzip test.zip test.bat > nul
  echo  done

At the writing this item a Turbo Pascal compiler can be obtained for
free on the net. For that and other information on Turbo Pascal
please see
 ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip
 Common Turbo Pascal Questions and Timo's answers
and the regular postings in the news:comp.lang.pascal.borland Usenet
newsgroup.

But the simplest solution is utilizing GAWK. The following example
batch demonstrates
  @echo off
  echo Testline 1|gawk '{printf"%%s",$0}'
  echo Testline 2|gawk '{printf"%%s",$0}'
  echo Testline 3|gawk '{printf"%%s\n",$0}'
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:41 2002
Subject: Function keys
Date: Fri, 1 Mar 2002 01:01:41
From: ts@uwasa.fi (Timo Salmi)

100. How can I detect the F1-F10 function keys in a batch?
==========================================================

As the tasks get more involved it is not necessarily a functional
idea always to force a pure MSDOS-original-commands-only solution,
except maybe for the sake of the challenge. Let's use a higher-level
language to prepare a rather simple auxiliary utility KEYLEVEL.EXE
for this task. The Turbo Pascal for the utility is given below, and
after that there is the test batch which shows you how to utilize
the programmed utility. If you do not have a Turbo Pascal compiler,
it can be obtained on the net (at the time of writing this item).
For more on finding the compiler please see the item #147 in
 ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip
 Common Turbo Pascal Questions and Timo's answers
and/or the regular postings in the news:comp.lang.pascal.borland
Usenet newsgroup.
   As usual, what is below is not the only possible solution. For
example if you have ANSI.SYS loaded you might temporarily redefine
the two-byte returning function keys as one-byte returning keys for
an easier detection as has been suggested on the Usenet news.

  program keylevel;
  uses FDelay, Crt, Dos;
  { See tsfaqp.zip item #124 for the fast PC FDdelay }
  {}
  procedure KEYS;
  var key : char;
  begin
    repeat
      if KeyPressed then
        begin
          key := ReadKey;
          case key of
             #0 : begin
                    key := ReadKey;
                    case key of
                      #59 : halt(59); { F1 }
                      #60 : halt(60); { F2 }
                      #61 : halt(61); { F3 }
                      #62 : halt(62); { F4 }
                      #63 : halt(63); { F5 }
                      #64 : halt(64); { F6 }
                      #65 : halt(65); { F7 }
                      #66 : halt(66); { F8 }
                      #67 : halt(67); { F9 }
                      #68 : halt(68); { F10 }
                      #71 : halt(71); { Home }
                      #72 : halt(72); { CursUp }
                      #73 : halt(73); { PageUp }
                      #75 : halt(75); { CursLf }
                      #77 : halt(77); { CursRg }
                      #79 : halt(79); { End }
                      #80 : halt(80); { CursDn }
                      #81 : halt(81); { PageDn }
                      #82 : halt(82); { Insert }
                      #83 : halt(83); { Delete }
                      else halt(1);
                    end; {case}
                  end;
             #3 : halt(3);  { ctrl-c }
            #13 : halt(13); { Return }
            #27 : halt(27); { Esc }
            else halt(0);
          end; {case}
        end; {if}
    until false;
  end;  (* keys *)
  {}
  begin
    KEYS;
  end.

And the test batch for finding which function key was pressed, which
you can then customize for your particular task:
  @echo off
  :_next
  echo Press a key (Esc to end):
  keylevel
  if errorlevel==0  if not errorlevel==1  echo Any other one-byte key
  if errorlevel==1  if not errorlevel==2  echo Any other two-byte key
  if errorlevel==3  if not errorlevel==4  echo Ctrl-C
  if errorlevel==13 if not errorlevel==14 echo Return
  if errorlevel==27 if not errorlevel==28 echo Esc
  if errorlevel==27 if not errorlevel==28 goto _end
  if errorlevel==59 if not errorlevel==60 echo F1
  if errorlevel==60 if not errorlevel==61 echo F2
  if errorlevel==61 if not errorlevel==62 echo F3
  if errorlevel==62 if not errorlevel==63 echo F4
  if errorlevel==63 if not errorlevel==64 echo F5
  if errorlevel==64 if not errorlevel==65 echo F6
  if errorlevel==65 if not errorlevel==66 echo F7
  if errorlevel==66 if not errorlevel==67 echo F8
  if errorlevel==67 if not errorlevel==68 echo F9
  if errorlevel==68 if not errorlevel==69 echo F10
  if errorlevel==71 if not errorlevel==72 echo Home
  if errorlevel==72 if not errorlevel==73 echo CursUp
  if errorlevel==73 if not errorlevel==74 echo PageUp
  if errorlevel==75 if not errorlevel==76 echo CursLf
  if errorlevel==77 if not errorlevel==78 echo CursRg
  if errorlevel==79 if not errorlevel==80 echo End
  if errorlevel==80 if not errorlevel==81 echo CursDn
  if errorlevel==81 if not errorlevel==82 echo PageDn
  if errorlevel==82 if not errorlevel==83 echo Insert
  if errorlevel==83 if not errorlevel==84 echo Delete
  goto _next
  :_end

So much for the Turbo Pascal version. For a QBASIC version a
compiled program is not needed, just the following batch:
  @echo off
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  :: First detect what key has been pressed
  >  tmp$$$.bas echo n$ = CHR$(0)
  >> tmp$$$.bas echo PRINT "Press a key: ";
  >> tmp$$$.bas echo DO UNTIL NOT LEN(a$) = 0
  >> tmp$$$.bas echo  a$ = INKEY$
  >> tmp$$$.bas echo LOOP
  >> tmp$$$.bas echo IF MID$(a$,1,1) = n$ THEN
  >> tmp$$$.bas echo   b$ = "Any other two-byte key"
  >> tmp$$$.bas echo ELSE
  >> tmp$$$.bas echo   b$ = a$
  >> tmp$$$.bas echo END IF
  >> tmp$$$.bas echo IF a$ = CHR$(3) THEN b$ = "Ctrl-C"
  >> tmp$$$.bas echo IF a$ = CHR$(13) THEN b$ = "Return"
  >> tmp$$$.bas echo IF a$ = CHR$(27) THEN b$ = "Esc"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(59) THEN b$ = "F1"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(60) THEN b$ = "F2"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(61) THEN b$ = "F3"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(62) THEN b$ = "F4"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(63) THEN b$ = "F5"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(64) THEN b$ = "F6"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(65) THEN b$ = "F7"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(66) THEN b$ = "F8"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(67) THEN b$ = "F9"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(68) THEN b$ = "F10"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(71) THEN b$ = "Home"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(72) THEN b$ = "CursUp"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(73) THEN b$ = "PageUp"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(75) THEN b$ = "CursLf"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(77) THEN b$ = "CursRg"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(79) THEN b$ = "End"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(80) THEN b$ = "CursDn"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(81) THEN b$ = "PageDn"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(82) THEN b$ = "Insert"
  >> tmp$$$.bas echo IF a$ = n$ + CHR$(83) THEN b$ = "Delete"
  ::
  :: Build an auxiliary batch to set the environment variable
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "set input="; b$
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program prepared in the above
  qbasic /run tmp$$$.bas
  ::
  :: Call the tmp###.bat batch created by the tmp$$$.bas QBASIC program
  call tmp###.bat
  ::
  :: Delete the auxiliary files
  for %%f in (tmp$$$.bas tmp###.bat) do del %%f
  ::
  :: Test the result
  echo.
  echo Your input was %input%
  set input=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:42 2002
Subject: Free space
Date: Fri, 1 Mar 2002 01:01:42
From: ts@uwasa.fi (Timo Salmi)

101. How can I get the free size and total size of a drive?
===========================================================

As in items #99 and #100 we'll use a Turbo Pascal solution. First
compile the Turbo Pascal program
  program freesize;
  uses Dos;
  var x     : longint;
      s     : string;
      drive : string;
      f     : text;
  begin
    if ParamCount > 0 then
      drive := ParamStr(1)  { A=1, B=2, C=3 }
      else drive := '@';    { default drive }
    Assign (f, 'tmp#$#$#.bat');
    Rewrite (f);
    writeln (f, '@echo off');
    x := DiskFree (ord(Upcase(drive[1]))-ord('A')+1);
    Str (x, s);
    writeln (f, 'set dfree_=', s);
    x := DiskSize (ord(Upcase(drive[1]))-ord('A')+1);
    Str (x, s);
    writeln (f, 'set dsize_=', s);
    Close (f);
  end.
Then you can apply the batch
  @echo off
  ::
  :: Get the drive
  set getdrv_=%1
  ::
  :: Call the environment variable setting TP program
  freesize %getdrv_%
  ::
  :: Call the batch written by the TP program
  call tmp#$#$#
  ::
  :: Show the results
  if not "%1"=="" echo %dfree_% free of %dsize_% on %getdrv_%:
  if "%1"=="" echo %dfree_% free of %dsize_% on default drive
  ::
  :: Clean up
  del tmp#$#$#.bat
  for %%v in (dfree_ dsize_ getdrv_) do set %%v=
If you wish to know how to get the size of a single file, see the
third option in item #95.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:43 2002
Subject: Search through files
Date: Fri, 1 Mar 2002 01:01:43
From: ts@uwasa.fi (Timo Salmi)

102. How can I search for a string through a directory of files?
================================================================

Ah, back to the simpler basics. Here goes. The FIND command does not
take wildcards to you'll have to apply a for loop:
  @echo off
  if exist tmp$$$.$$$ del tmp$$$.$$$
  for %%f in (*.txt) do find "Whatever" %%f >> tmp$$$.$$$
  type tmp$$$.$$$ | more
  if exist tmp$$$.$$$ del tmp$$$.$$$
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:44 2002
Subject: Numeric extension + 1
Date: Fri, 1 Mar 2002 01:01:44
From: ts@uwasa.fi (Timo Salmi)

103. How do I find the largest numeric extension and add +1 to it?
==================================================================

This is a backup related problem which involves:
 - Making a sorted directory list,
 - detecting files with all-numerical extensions such as 003,
 - finding the last of these files,
 - extracting the extension,
 - adding +1 to the extension variable.
As a further complication the leading zeros must not be lost.
G(nu)AWK will be assumed available.
  @echo off
  ::
  dir/b/o:e|gawk -F. '$2!~"[A-Z]"{printf "@set last_=%%s\n",$0}'>tmp$$$.bat
  call tmp$$$.bat
  if not "%last_%"=="" echo The last all-number-extension filename is %last_%
  if "%last_%"=="" echo No all-number-extension filename
  ::
  echo %last_%|gawk -F. '{printf "set exten_=%%s\n",$2}'>tmp$$$.bat
  call tmp$$$.bat
  if not "%exten_%"=="" echo The extension is %exten_%
  if "%exten_%"=="" echo No extension
  ::
  echo %exten_%|gawk '{i="000"$1+1}{p=length(i)}{printf "@set exten1_=%%s\n",substr(i,p-2,p)}'>tmp$$$.bat
  call tmp$$$.bat
  if not "%exten1_%"=="" echo The extension + 1 is %exten1_%
  if "%exten1_%"=="" echo No extension
  ::
  for %%v in (last_ exten_ exten1_) do set %%v=
  if exist tmp$$$.bat del tmp$$$.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:45 2002
Subject: Running the latest exe
Date: Fri, 1 Mar 2002 01:01:45
From: ts@uwasa.fi (Timo Salmi)

104. How do I run the latest executable from the default directory?
===================================================================

G(nu)AWK will be assumed available.
  @echo off
  ::
  if not exist *.exe goto _nofile
  ::
  echo {printf "@set last_=%%s\n",$1}>tmp$$$.awk
  dir *.exe /b/o:d|gawk -ftmp$$$.awk>tmp$$$.bat
  call tmp$$$.bat
  ::
  choice /c:yn "Run %last_%"
  if errorlevel 2 goto _clean
  %last_%
  ::
  :_clean
  for %%f in (bat awk) do if exist tmp$$$.%%f del tmp$$$.%%f
  goto _out
  ::
  :_nofile
  Echo No *.exe files found
  ::
  :_out
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:46 2002
Subject: Decting ANSI.SYS
Date: Fri, 1 Mar 2002 01:01:46
From: ts@uwasa.fi (Timo Salmi)

105. How can I determine if ANSI.SYS is loaded?
===============================================

You can utilize MEM and the errorlevel returned by the FIND command
just the same way as we did in the earlier item #61.
  @echo off
  mem /c | find " ANSI " > nul
  if errorlevel==0 if not errorlevel==1 echo ANSI.SYS is loaded
  if errorlevel==1 if not errorlevel==2 echo ANSI.SYS is not loaded
It is easy to see that the trick applies to other drivers and
programs reported by MEM /C(LASSIFY).

Next let's continue the question by asking now much memory the
loaded ANSI.SYS takes up. The auxiliary Unix-like programs gawk and
sed are assumed available.
  @echo off
  mem /c|find " ANSI "|gawk '{print $2}'|sed -e "s/\.//g"
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:47 2002
Subject: Detecting the last drive
Date: Fri, 1 Mar 2002 01:01:47
From: ts@uwasa.fi (Timo Salmi)

106. How do I get the last drive into an environment variable?
==============================================================

Assume that G(nu)AWK is available.
  @echo off
  mem /d|find "LASTDRIVE"|gawk -F= '{printf "@set last_=%%s\n",$2}'>tmp$$$.bat
  call tmp$$$.bat
  ::
  :: Test the result
  echo %last_%
  ::
  :: Clean up
  if exist tmp$$$.bat del tmp$$$.bat
  set last_=

Note however. The lastdrive parameter is NOT necessarily the same
thing as the last valid drive letter. It is just a parameter that
gives what the last drive is allowed to be at most. Testing the
validity of a drive letter is a separate consideration, as given
below.
  @echo off
  mem /d|find "LASTDRIVE"|gawk -F= '{printf "@set last_=%%s\n",$2}'>tmp$$$.bat
  call tmp$$$.bat
  ::
  :: Test the result
  echo %last_%
  ctty nul
  cd %last_%:|find ":\"
  ctty con
  if errorlevel==0 if not errorlevel==1 echo Drive %last_% is valid
  if errorlevel==1 echo Drive %last_% is Not valid
  ::
  :: Clean up
  if exist tmp$$$.bat del tmp$$$.bat
  set last_=

Another, very similar option for finding out the lastdrive parameter
is checking the CONFIG.SYS file:
  @echo off
  find "LASTDRIVE="<c:\config.sys |sed -e "s/LAST/set LAST/">tmp$$$.bat
  call tmp$$$.bat
  ::
  :: Test and clean up
  echo The last drive in config.sys is %LASTDRIVE%
  if exist tmp$$$.bat del tmp$$$.bat
  set LASTDRIVE=

Let's consider finding the last drive available instead of the
LASTDRIVE parameter. Below is one rather brute force approach. It
may not detect a CD-ROM drive.
  @echo off
  set drive_=C
  goto _subru
  :_c
  set drive_=D
  goto _subru
  :_d
  set drive_=E
  goto _subru
  :_e
  set drive_=F
  goto _subru
  :_f
  set drive_=G
  goto _subru
  :_g
  set drive_=H
  goto _subru
  :_h
  set drive_=I
  goto _subru
  :_i
  set drive_=J
  goto _subru
  :_j
  set drive_=K
  goto _subru
  :_k
  set drive_=L
  goto _subru
  :_l
  set drive_=M
  goto _subru
  :_m
  set drive_=N
  goto _subru
  :_n
  set drive_=O
  goto _subru
  :_o
  set drive_=P
  goto _subru
  :_p
  set drive_=Q
  goto _subru
  :_q
  set drive_=R
  goto _subru
  :_r
  set drive_=S
  goto _subru
  :_s
  set drive_=T
  goto _subru
  :_t
  set drive_=U
  goto _subru
  :_u
  set drive_=V
  goto _subru
  :_v
  set drive_=W
  goto _subru
  :_w
  set drive_=X
  goto _subru
  :_x
  set drive_=Y
  goto _subru
  :_y
  set drive_=Z
  goto _subru
  :_z
  goto _out
  ::
  :_subru
  find "" %drive_%:\nul>nul
  if errorlevel=2 goto _2
  set last_=%drive_%
  goto _return
  :_2
  echo Drive %drive_%: not found
  :_return
  goto _%drive_%
  ::
  :_out
  echo.
  echo The last drive found was %last_%:
  set drive_=
  set last_=

Alternatively
  @echo off
  for %%d in (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 if exist %%d:\nul set last_=%%d
  echo The last drive found was %last_%:
  set last_=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:48 2002
Subject: Number of lines in a file
Date: Fri, 1 Mar 2002 01:01:48
From: ts@uwasa.fi (Timo Salmi)

107. How can I calculate the number of lines in a file?
=======================================================

A QBASIC solution for this problem is
  @echo off
  if "%1"=="" goto _usage
  if not exist %1 goto _notfound
  ::
  > tmp$$$.bas echo LET n = 0
  >>tmp$$$.bas echo OPEN "%1" FOR INPUT AS #1
  >>tmp$$$.bas echo DO
  >>tmp$$$.bas echo   LINE INPUT #1, a$
  >>tmp$$$.bas echo   LET n = n + 1
  >>tmp$$$.bas echo LOOP UNTIL (EOF(1))
  >>tmp$$$.bas echo CLOSE #1
  >>tmp$$$.bas echo OPEN "tmp$$$.bat" FOR OUTPUT AS #2
  >>tmp$$$.bas echo PRINT #2, "@set lines_=";LTRIM$(STR$(n))
  >>tmp$$$.bas echo CLOSE #2
  >>tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program and the batch it prepared
  qbasic /run tmp$$$.bas
  call tmp$$$.bat
  ::
  :: Show the results
  echo The length of file %1 is %lines_% lines
  goto _out
  ::
  :_usage
  echo Usage: %0 [FileName]
  goto _out
  ::
  :_notfound
  echo File %1 not found
  goto _out
  ::
  :_out
  set lines_=
  for %%f in (tmp$$$.bas tmp$$$.bat) do if exist %%f del %%f

A G(nu)AWK and SED solution would be
  @echo off
  if "%1"=="" goto _usage
  if not exist %1 goto _notfound
  ::
  find /n/v "#?\@$&/(.~^}){" %1|gawk -F] '{printf "set lines_=%%s\n",$1}'|sed -e "s/\[//">tmp$$$.bat
  ::
  :: Show the results
  call tmp$$$.bat
  echo The length of file %1 is %lines_% lines
  goto _out
  ::
  :_usage
  echo Usage: %0 [FileName]
  goto _out
  ::
  :_notfound
  echo File %1 not found
  goto _out
  ::
  :_out
  set lines_=
  if exist tmp$$$.bat del tmp$$$.bat

Another, similar version with ECHON.EXE instead of GAWK
  @echo off
  if "%1"=="" goto _usage
  echon @set length_=>tmp$$$.bat
  find /n/v "" %1|sed -n $p|sed -e "s/\].*//" -e "s/\[//">>tmp$$$.bat
  call tmp$$$.bat
  echo File %1 length_=%length_%
  goto _out
  :_usage
  echo Usage: %0 [FileName]
  :_out
  set length_=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
If you wish to unravel what the crucial line
  find /n/v "" %1|sed -n $p|sed -e "s/\].*//" -e "s/\[//"
does, experiment with find /n/v "" YourFile, then add one part after
another to the line and see what each step produces.

A Turbo Pascal solution of this task would be
  @echo off
  ::
  :: Usage and availability
  if "%1"=="" goto _usage
  if not exist %1 goto _nofile
  ::
  :: Ensure that the Turbo Pascal compiler is available
  set found_=
  if exist tpc.exe set found_=yes
  for %%d in (%path%) do if exist %%d\tpc.exe set found_=yes
  for %%d in (%path%) do if exist %%dtpc.exe set found_=yes
  if not "%found_%"=="yes" goto _notpc
  ::
  :: Ensure a .bat extension when using this batch
  echo %0 | find /i ".bat" > nul
  if errorlevel==0 if not errorlevel==1 set batname=%0
  if errorlevel==1 set batname=%0.bat
  ::
  :: Build a Turbo Pascal program to a file tmp$$$.pas
  set skip=
  find "{T%skip%P}" <%batname% > tmp$$$.pas
  goto _jump
  ::
  Uses Dos; {TP}
  var n : longint; {TP}
      s : string; {TP}
  begin {TP}
    n := 0; {TP}
    while not eof(input) do {TP}
      begin {TP}
        readln (s); {TP}
        Inc(n); {TP}
    end; {TP}
    Writeln ('@set lines_=', n); {TP}
  end. {TP}
  ::
  :: Compile the Turbo Pascal program
  :_jump
  tpc tmp$$$.pas>nul
  ::
  :: Apply and show the results (Don't use the name tmp$$$.bat)
  tmp$$$.exe < %1 > tmp###.bat
  call tmp###.bat
  echo The length of file %1 is %lines_% lines
  goto _out
  ::
  :: Usage error warning
  :_usage
  echo Usage: %0 [FileName]
  goto _out
  ::
  :: Missing file warning
  :_nofile
  echo File %1 not found
  goto _out
  ::
  :: Missing compiler warning
  :_notpc
  echo Turbo Pascal compiler TPC.EXE not found
  goto _out
  ::
  :: Clean up
  :_out
  for %%f in (tmp$$$.pas tmp$$$.exe tmp###.bat) do if exist %%f del %%f
  for %%v in (batname found_ lines_) do set %%v=

The simples GAWK-based solution is
  @echo off
  type myfile.txt|gawk '{printf "@set lines_=%%s\n", NR}'>tmp$$$.bat
  call tmp$$$.bat
  echo Number of lines = %lines_%
  ::
  :: Clean up
  del tmp$$$.bat
  set lines_=
Or for even simpler and more speed instead substitute the GAWK line
  type tmp$$$.txt|gawk 'BEGIN{} END{printf "@set lines_=%%s\n",NR}'>tmp$$$.bat
or, even more briefly
  type tmp$$$.txt|gawk 'END{printf "@set lines_=%%s\n",NR}'>tmp$$$.bat
See the item #118 for an application of this information.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:49 2002
Subject: Restoring command history
Date: Fri, 1 Mar 2002 01:01:49
From: ts@uwasa.fi (Timo Salmi)

108. Is it possible to save and later restore the command history?
==================================================================

As you know, in MS-DOS the memory-resident DOSKEY utility can be
used to store, recall and edit commands. You can recall the command
history by pressing F7, or you can get a list of the command history
and e.g. direct it to a file using
  DOSKEY /HISTORY > DOSKEY.HIS
If you have erased the doskey commands from the memory (ALT+F7),
there is no way to restore the command history into the memory like
e.g. in 4dos. However, if you have directed the command history to a
file it is possible to browse that file, and cut and paste commands
from there. If you are running MS-DOS in a Windows dosbox, you can
put a command into the clipboard. Many, editors have a facility to
copy text to the windows clipboard. The DOSHIS.BAT batch file
included in this collection organizes thus handling the command
history.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:50 2002
Subject: Changing just one line
Date: Fri, 1 Mar 2002 01:01:50
From: ts@uwasa.fi (Timo Salmi)

109. Is it possible to change just one line of a file with a batch?
===================================================================

This is indeed a very frequently asked question. In practice a third
party utility is the best solution. Let's assume that we have
SED.EXE available. Although this is an MS-DOS batch collection, not
a SED tutorial, let's look at an example of using this program to do
the task. Assume that we wish to change or replace a line in the
CONFIG.SYS. The example contents of the CONFIG.SYS could be
  DEVICE=C:\WINDOWS\ES1868.COM /A:0 /I:0
  device=C:\WINDOWS\HIMEM.SYS /testmem:off
  device=c:\dos\emm386.exe noems
  dos=high,umb
  files=50
  buffers=10
  lastdrive=o
  DEVICEhigh=C:\BTCCDROM\BTCCDROM.SYS /D:BTCCD001 /V
  DEVICE=C:\WINDOWS\IFSHLP.SYS
  STACKS=9,256
Say that you wanted for whatever reason to alter the line
  files=50
istead to be
  files=60
Some alternative SED solutions are given below.
   It is obvious that you want to change the target line, and the
target line only. If you can trust that the line will be the 5th
line, then use
  sed -e "5{s/50/60/;}" config.sys
A very similar option is
  sed -e "5{s/.*/files=60/;}" config.sys
The subtle difference is that the entire line is being replaced. In
a third option the location of the line is not presumed, but the
line is searched for
  sed -e "/^files=/{s/50/60/;}" config.sys
These examples should give you the general idea how to proceed from
here. Building the system to replace the old CONFIG.SYS with the new
one without getting the files confused is the remaining step. E.g.
you might have
  @echo off
  sed -e "/^files=/{s/.*/files=60/;}" config.sys > tmp$$$.sys
  copy /-y tmp$$$.sys config.sys
  del tmp$$$.sys

The cumbersome EDLIN editor can be used for the same effect. The
solution is given below. First, however, see the end of item #110
for salient information about making EDLIN available.
  @echo off
  copy config.sys config.new
  echo sfiles=50> edlin.cmd
  echo d >> edlin.cmd
  echo files=60>tmp$$$.$$$
  echo t tmp$$$.$$$ >> edlin.cmd
  echo e >> edlin.cmd
  edlin config.new < edlin.cmd
  echo.
  echo config.new ready
  for %%f in (edlin.cmd config.bak tmp$$$.$$$) do del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:51 2002
Subject: Deleting by line numbers
Date: Fri, 1 Mar 2002 01:01:51
From: ts@uwasa.fi (Timo Salmi)

110. Is it possible to delete lines by their line numbers only?
===============================================================

Consider deleting the second and the fourth line of a file.
Let's assume that we have available the third party utility
GAWK.EXE. Then it is simply
  gawk 'NR!=2 && NR!=4 {printf "%%s\n",$0}' myfile.txt

With SED.EXE you would have it a bit more complicated
  sed 2d myfile.txt | sed 3d
or, alternatively
  sed -n 2!p myfile.txt | sed -n 3!p
But there is a simpler syntax for the SED alternative as pointed out
by Laura Fairhead, which I originally failed to note. It is
  sed "2d;4d" myfile.txt

The QBASIC solution would be rather cumbersome, but nearer plain
MS-DOS
  @echo off
  if "%1"=="" goto _usage
  if not exist %1 goto _notfound
  ::
  > tmp$$$.bas echo LET n = 0
  >>tmp$$$.bas echo OPEN "%1" FOR INPUT AS #1
  >>tmp$$$.bas echo DO
  >>tmp$$$.bas echo   LINE INPUT #1, a$
  >>tmp$$$.bas echo   LET n = n + 1
  >>tmp$$$.bas echo   IF NOT n = 2 AND NOT n = 4 THEN PRINT a$
  >>tmp$$$.bas echo LOOP UNTIL (EOF(1))
  >>tmp$$$.bas echo CLOSE #1
  >>tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program and the batch it prepared
  qbasic /run tmp$$$.bas
  goto _out
  ::
  :_usage
  echo Usage: %0 [FileName]
  goto _out
  ::
  :_notfound
  echo File %1 not found
  goto _out
  ::
  :_out
  set lines_=
  for %%f in (tmp$$$.bas) do if exist %%f del %%f
If you wish to store the output, direct the PRINT to a file. See the
other items in this FAQ with QBASIC for the details.

The older MS-DOS versions included the notorious EDLIN editor up
until and including MS-DOS version 5.0. The EDLIN editor can be used
in batches to manipulate files. However, if you are using a later
MS-DOS version you have to load SETVER in your CONFIG.SYS file in
order the utilize EDLIN. E.g. if your MS-DOS version is 6.22, you
must first get EDLIN.EXE from version 5.0 and include in your
CONFIG.SYS the line:
  device=c:\dos\setver.exe
Alternatively, if you have MS-DOS 6.22, get the supplemental 6.22
disk from their server, if available. Then you'll not need the
SETVER trick.
The edlin solution for the task at hand would be
  @echo off
  copy myfile.txt myfile.new
  echo l   > edlin.cmd
  echo 2d >> edlin.cmd
  echo 3d >> edlin.cmd
  echo e  >> edlin.cmd
  edlin myfile.new < edlin.cmd
  echo.
  echo myfile.new ready
  for %%f in (edlin.cmd myfile.bak) do del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:52 2002
Subject: Detecting Ram Drive
Date: Fri, 1 Mar 2002 01:01:52
From: ts@uwasa.fi (Timo Salmi)

111. How can I tell what drive has been allocated by ramdrive.sys?
==================================================================

First of all, let us assume plain MS-DOS (6.22). The task is not
easy without a third party utility, but here are some ideas. To
begin with, one can detect whether ramdrive.sys device driver has
been loaded by something like
  mem /c | find "RAMDRIVE" > nul
  if errorlevel==2 goto _nofind
  if errorlevel==1 goto _nofind
  echo RAMDRIVE is loaded
  goto _end
  :_nofind
  echo RAMDRIVE is not loaded
  :_end
Then one can use MSD.EXE /P MSD.LOG to create a system information
file and search and extract information from the line like
        M:   RAM Disk       15M        15M
The relevant batch would then be
  mem /c | find "RAMDRIVE" > nul
  if errorlevel==2 goto _nofind
  if errorlevel==1 goto _nofind
  find "RAM Disk" MSD.LOG|gawk '{ printf "@set ram_=%%s\n",$1 }'>tmp$$$.bat
  goto _common
  :_nofind
  echo @set ram_=None>tmp$$$.bat
  :_common
  call tmp$$$.bat
  echo RAMDRIVE.SYS RAM Disk is %ram_%
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  set ram_=

But if one really needs a ramdrive detector, it is best to write one
in a higher level language. Covered e.g. in item #154 of
 ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip
 Common Turbo Pascal Questions and Timo's answers
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:53 2002
Subject: Inserting a line
Date: Fri, 1 Mar 2002 01:01:53
From: ts@uwasa.fi (Timo Salmi)

112. How can in insert a line in the middle of a file with a batch?
===================================================================

Using SED this is rather easy. Say that you wish to insert something
between the fourth and the fifth line of your file. Below is how to
do it:

  @echo off
  sed -n 1,4p myfile.txt>newfile.txt
  echo Insert whatever>>newfile.txt
  sed -n 5,$p myfile.txt>>newfile.txt
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:54 2002
Subject: MS-DOS vs. various Windows
Date: Fri, 1 Mar 2002 01:01:54
From: ts@uwasa.fi (Timo Salmi)

113. What is difference between MS-DOS and various Windows batches?
===================================================================

This is a somewhat touchy subject in the Usenet newsgroups
 news:alt.msdos.batch
 news:alt.msdos.batch.nt
I will not try a resolution. However, the reference below is useful
for a concise understanding the categories. My compliments to Walter
Briscoe.
<http://groups.google.com/groups?as_umsgid=9q6u8e%248b3%40poiju.uwasa.fi>
I, too, think that the distiction should be based on the command
interpreter used, and version, and their compatibility.

A further, related tip. Some users' ISPs do not carry the latter
newsgroup, i.e. the NT batches newsgroup. It can alternatively be
accessed through the Usenet news repository
<http://groups.google.com/groups?as_ugroup=alt.msdos.batch.nt&as_scoring=d>

One way of looking the issue is recalling that COMMAND.COM is the
original MS-DOS command interpreter. Alternatives, such as 4DOS or
CDM.EXE are interesting and useful, but they are subsitutes for the
original. Furthermore, bear in mind that the file system Windows
3.11/95/98/Me are still FAT-based while NT/2000/XP are NTFS-based.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:55 2002
Subject: Comments from CONFIG.SYS
Date: Fri, 1 Mar 2002 01:01:55
From: ts@uwasa.fi (Timo Salmi)

114. Is it possible to display comments from the config.sys file?
=================================================================

See HELP CONFIG.SYS and there the information about ? the
information about SET. Then see
<http://groups.google.com/groups?as_umsgid=3BBC8319.B638C2F5%40yahoo.com>
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:56 2002
Subject: Continuing a line
Date: Fri, 1 Mar 2002 01:01:56
From: ts@uwasa.fi (Timo Salmi)

115. Can I split a single command over multiple lines in a batch?
=================================================================

In a sense, yes, but the alternatives are ugly and kludgy. Here is
one such based on my ECHON.EXE (see item #99). Note that the very
restrictive total length limitation of an MS-DOS command line still
remains.

  @echo off
  > tmp$$$.bat echon @echo First part
  >>tmp$$$.bat echon  Second part
  >>tmp$$$.bat echon  Third part
  >>tmp$$$.bat  echo  Yuck!
  call tmp$$$
  ::
  rem Clean up
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

The same task using G(nu)AWK:
  @echo off
  > tmp$$$.bat echo.|gawk '{printf"@echo First part"}'
  >>tmp$$$.bat echo.|gawk '{printf"  Second part"}'
  >>tmp$$$.bat echo.|gawk '{printf"  Third part"}'
  >>tmp$$$.bat echo.|gawk '{printf"  Yuck!\n"}'
  call tmp$$$
  ::
  rem Clean up
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

But if the total length of the parsed batch line exceeds the allowed
maximum of an MS-DOS batch line, then you have to logically divide
the task on the line into several parts.

In this connection it is often asked if there is a specific
continuation token for MS-DOS batches line there is \ at the end of
an Unix script. The answer is a definite no. The only way to
"continue" a line is to divide the task logically onto several
lines. For example, consider the task of combining a number of text
files like this
  copy myfile1.txt+myfile2.txt+myfile3.txt all.txt
Ok, for brevity there are just three files there, but consider the
principle. The user would like do do it like this [SIC]
  copy myfile1.txt \
       +myfile2.txt \
       +myfile3.txt all.txt
In MS-DOS batches that is incorrect! One can't continue a line! But
a similar result can be achieved by logically dividing the task into
several parts. One option is
  type myfile1.txt >all.txt
  type myfile2.txt>>all.txt
  type myfile3.txt>>all.txt
Of course, the solution, and even the possibility of a solution,
will vary from one task to another. In this particular case there
would be e.g. the following simple option, even if this is slightly
beside the actual point of the continuation
  copy myfile?.txt all.txt
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:57 2002
Subject: Yesterday's date
Date: Fri, 1 Mar 2002 01:01:57
From: ts@uwasa.fi (Timo Salmi)

116. How can I get yesterday's date into an environment variable?
=================================================================

This is far from an easy task because of the carry-over effects at
the end of a month (and a year). An auxiliary calendar program is
needed. One solution is given below. Note that the method could also
be used to get today's date (+0), tomorrow's date (+1), and so on.
  @echo off
  ::
  rem What was yesterday's date?
  rem Uses WHATDATE.EXE
  whatdate -1 /e > nul
  echo whatdate_=%whatdate_%
  ::
  rem How the whatdate_ variable can be divided into three parts:
  rem Uses ECHON.EXE and CUT.EXE
  >  tmp$$$.bat
  >> tmp$$$.bat echo @echo off
  >> tmp$$$.bat echon set yy_=
  >> tmp$$$.bat echo %whatdate_%|cut 1 2
  >> tmp$$$.bat echon set mm_=
  >> tmp$$$.bat echo %whatdate_%|cut 2 3
  >> tmp$$$.bat echon set dd_=
  >> tmp$$$.bat echo %whatdate_%|cut 5 6
  call tmp$$$.bat
  ::
  rem Show the results
  echo yy_=%yy_% mm_=%mm_% dd_=%dd_%
  ::
  rem Clean up
  for %%v in (whatdate_ yy_ mm_ dd_) do set %%v=
  for %%f in (tmp$$$.bat) do if exist %%f del %%f

The above uses WHATDATE.EXE Date +- number of days from now
 ftp://garbo.uwasa.fi/pc/ts/tsutlc26.zip
 Timo's 3rd utility set (dirf,dirinfo,doubles,hidden,split,...)
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:58 2002
Subject: Get a word from line
Date: Fri, 1 Mar 2002 01:01:58
From: ts@uwasa.fi (Timo Salmi)

117. How do I get the 3rd word from the 5th line of a text file?
================================================================

Consider the demonstration below
  @echo off
  rem Make a test file
  echo   word11 word12 word13 word14>tmp$$$.txt
  for %%i in (2 3 4 5 6 7) do echo   word%%i1 word%%i2 word%%i3 word%%i4>>tmp$$$.txt
  type tmp$$$.txt
  ::
  echo.
  echo SED Output 5th line
  sed -n 5p tmp$$$.txt
  ::
  echo.
  echo SED Output 5th line; Filter leading blanks
  sed -n 5p tmp$$$.txt|sed "s/^ *//"
  ::
  echo.
  echo SED Output 1st word 5th line
  sed -n 5p tmp$$$.txt|sed "s/^ *//;s/ .*$//"
  ::
  echo.
  echo SED Output last word 5th line
  sed -n 5p tmp$$$.txt|sed "s/^ *//;s/[^ ].* //"
  ::
  echo.
  echo GAWK Output 5th line
  gawk 'NR==5 {printf "%%s\n",$0}' tmp$$$.txt
  ::
  echo.
  echo GAWK Output 1st word 5th line
  gawk 'NR==5 {printf "%%s\n",$1}' tmp$$$.txt
  ::
  echo.
  echo GAWK Output 3rd word 5th line
  gawk 'NR==5 {printf "%%s\n",$3}' tmp$$$.txt
  ::
  echo.
  echo GAWK Output last word 5th line
  gawk 'NR==5 {printf "%%s\n",$NF}' tmp$$$.txt

A somewhat more complicated task is to find and replace a single
item in a text file. Let's take an example. Assume we have a text
file
  Line 1
  Line 2
  Line 3
  Line 4
We wish to replace the "1" with "one" on the first line, and "2"
with "two" on the second line. This is one way of doing it:
  @echo off
  ::
  :: Build a demonstration textfile
  > myfile.txt echo Line 1
  >>myfile.txt echo Line 2
  >>myfile.txt echo Line 3
  >>myfile.txt echo Line 4
  ::
  :: Build an awk script
  > tmp$$$.awk echo {
  >>tmp$$$.awk echo if(NR==1){$2 = "one"}
  >>tmp$$$.awk echo if(NR==2){$2 = "two"}
  >>tmp$$$.awk echo printf $0 "\n"
  >>tmp$$$.awk echo }
  ::
  :: Run the script and the batch it has made (direct wherever)
  <myfile.txt gawk -f tmp$$$.awk
  ::
  :: Clean up
  for %%f in (tmp$$$.awk myfile.txt) do del %%f

Consider another example with a slightly different find and replace
task. Assume that somewhere in a text file there is a unique line
"file info = xxxx". You wish to replace the "xxxx", i.e. the fourth
item on the line with "9999". This is how to proceed in that case:
  @echo off
  ::
  :: Build a demonstration textfile
  > myfile.txt echo Line 1
  >>myfile.txt echo Line 2
  >>myfile.txt echo file info = xxxx
  >>myfile.txt echo Line 4
  ::
  :: Build an awk script
  > tmp$$$.awk echo {
  >>tmp$$$.awk echo if($0~"file info =")
  >>tmp$$$.awk echo {$4 = "9999"}
  >>tmp$$$.awk echo printf $0
  >>tmp$$$.awk echo printf "\n"
  >>tmp$$$.awk echo }
  ::
  :: Run the script and the batch it has made (direct wherever)
  <myfile.txt gawk -f tmp$$$.awk
  ::
  :: Clean up
  for %%f in (tmp$$$.awk myfile.txt) do del %%f

A related FAQ is putting the n:th line into an environment variable.
Below is one option (let's assume for simplicity that n is 3).
  @echo off
  ::
  :: Build a demonstration textfile
  > myfile.txt echo Line 1
  >>myfile.txt echo Line 2
  >>myfile.txt echo Line 3
  >>myfile.txt echo Line 4
  ::
  :: Build an awk script
  > tmp$$$.awk echo {
  >>tmp$$$.awk echo if(NR==3)
  >>tmp$$$.awk echo {printf"@set myvar_=%%s\n",$0}
  >>tmp$$$.awk echo }
  ::
  :: Run the script and the batch it has made (direct wherever)
  <myfile.txt gawk -f tmp$$$.awk>tmp$$$.bat
  call tmp$$$
  ::
  :: Show the result
  echo myvar=_%myvar_%
  ::
  :: Clean up
  for %%f in (tmp$$$.bat tmp$$$.awk myfile.txt) do del %%f
  for %%v in (myvar_) do set %%v=
Of course, in the strict sense making a separate awk script is
unnecessary, and used in the above for demonstration.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:01:59 2002
Subject: Input from text file
Date: Fri, 1 Mar 2002 01:01:59
From: ts@uwasa.fi (Timo Salmi)

118. How to put the content of text file into a variable?
=========================================================

Q: I want to put the contents of text file into a variable. I tried
TYPE file.txt > %TEST% but that did not work.

A: This is how to solve it
  @echo off
  echon set test=>tmp$$$.bat
  type file.txt>>tmp$$$.bat
  call tmp$$$.bat
  echo test=%test%
  del tmp$$$.bat

But, it is not usually quite that simple. The above requires 1) that
the text is on the first line and 2) that there is only one line.
However, this situation is solvable. Also multiple lines can be
handled relatively easily by concatenation using a "paste" Unix port
like CONCAT.EXE "Join two text-files side by side". First, say there
are exactly _two_ lines. Then
  @echo off
  echo set first_=>tmp$$$.1
  echo set second_=>>tmp$$$.1
  concat tmp$$$.1 file.txt>tmp$$$.bat
  call tmp$$$
  echo first_=%first_%
  echo second_=%second_%
  for %%v in (first_ second_) do set %%v=
  for %%f in (tmp$$$.*) do if exist %%f del %%f

If there are more than two lines in file.txt one can ensure the
exact two lines using SED.EXE
  type file.txt|sed -n 1,2p>tmp$$$.2
  concat tmp$$$.1 tmp$$$.2>tmp$$$.bat

The same, naturally, goes for ensuring that there is just the one
line, which we originally started the discussion from. So voila :-)
  sed -n 1p

If you first wish to delete all the potential empty and rem lines
from your file.txt file run it through
  sed "/^$/d;/^.*[rR][eE][mM]/d"
or
  sed "/^ *$/d;/^.*[rR][eE][mM]/d"
if you consider lines with spaces only as blanks.

A GAWK-based solution for putting say the first line of a file into
an environment variable is
  @echo off
  type myfile.txt|gawk 'NR==1{printf "@set first_=%%s\n",$0}'>tmp$$$.bat
  for %%c in (call del) do %%c tmp$$$.bat
  :: Test it
  echo The first line is = %first_%
  :: Clean up
  set first_=

What about the last line?
  @echo off
  type myfile.txt|gawk '{printf "@set last_=%%s\n",$0}'>tmp$$$.bat
  for %%c in (call del) do %%c tmp$$$.bat
  :: Test it
  echo The last line is = %last_%
  :: Clean up
  set last_=

What about the last but one?
  @echo off
  type myfile.txt|gawk 'END{printf "@set lines_=%%s\n",NR-1}'>tmp$$$.bat
  for %%c in (call del) do %%c tmp$$$.bat
  ::
  type myfile.txt|gawk 'NR==%lines_%{printf "@set line_=%%s\n",$0}'>tmp$$$.bat
  for %%c in (call del) do %%c tmp$$$.bat
  echo The desired line is = %line_%
  ::
  :: Clean up
  for %%v in (lines_ line_) do set %%v=
If you want just, say, the second word on the line last but one,
then replace the $0 with $2 in the above.

There is another way of getting the last line but one with G(nu)AWK.
  type myfile.txt|gawk '{prev=last}{last=$0}END{printf "%%s\n",prev}'

Also see the later item #137.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:00 2002
Subject: Oldest and newest files
Date: Fri, 1 Mar 2002 01:02:00
From: ts@uwasa.fi (Timo Salmi)

119. How do I get the three oldest and latest files of a directory?
===================================================================

The last solution is slightly country specific and assumes that the
time separator is ":".
  @echo off
  echo Three oldest files in the current directory
  dir *.* /o:d|find/v "Directory of"|find /v "."|find ":"|sed -n 1,3p
  ::
  echo.
  echo Three newest files in the current directory
  dir *.* /o:-d|find/v "Directory of"|find /v "."|find ":"|sed -n 1,3p

In bare format, and this time in lower case
  @echo off
  echo Three oldest files in the current directory
  dir *.* /o:d/b/l|sed -n 1,3p
  ::
  echo.
  echo Three newest files in the current directory
  dir *.* /o:-d/b/l|sed -n 1,3p

If you want to have the path included in the bare format (and do not
wish to recurse the subdiretoctiries) then, e.g.
  @echo off
  echon set dir_=>tmp$$$.bat
  cd>>tmp$$$.bat
  call tmp$$$
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  ::
  echo Three oldest files in the current directory
  dir *.* /o:d/b|sed -n 1,3p|gawk '{printf "%dir_%\\%%s\n",$0}'
  ::
  echo.
  echo Three newest files in the current directory
  dir *.* /o:-d/b|sed -n 1,3p|gawk '{printf "%dir_%\\%%s\n",$0}'
  ::
  set dir_=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:01 2002
Subject: ASCII table
Date: Fri, 1 Mar 2002 01:02:01
From: ts@uwasa.fi (Timo Salmi)

120. Hint: Displaying an ASCII (hex) value table
================================================

Sometimes it might be useful to be quickly to display the table of
ASCII characters and their hexadecimal values. Here is a batch to do
that. It requires G(nu)AWK.
  @echo off
  echo.>tmp$$$.bat
  echo 0     1
  echo 1234560123456789CDEF
  for %%h in ( 01 02 03 04 05 06) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( 10 11 12 13 14 15 16 17 18 19 1C 1D 1E 1F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  echo.
  echo.
  echo 2               3               4
  echo 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
  for %%h in ( 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  echo.
  echo.
  echo 5               6               7
  echo 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
  for %%h in ( 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  echo.
  echo.
  echo 8               9               A
  echo 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
  for %%h in ( 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  echo.
  echo.
  echo B               C               D
  echo 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
  for %%h in ( B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  echo.
  echo.
  echo E               F
  echo 0123456789ABCDEF0123456789ABCDE
  for %%h in ( E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  for %%h in ( F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE) do gawk '{printf "%%s", "\x%%h"}' tmp$$$.bat
  echo.
  for %%f in (tmp$$$.txt) do if exist %%f del %%f

With GAWK one could also write e.g.
  @echo off
  ::
  >  tmp$$$.awk echo {
  >> tmp$$$.awk echo for (i=32;i!=256;i++)
  >> tmp$$$.awk echo   printf "%%s %%2x %%2c\n",i,i,i
  >> tmp$$$.awk echo }
  ::
  echo.|gawk -f tmp$$$.awk|more
  ::
  for %%f in (tmp$$$.awk) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:03 2002
Subject:
Date: Fri, 1 Mar 2002 01:02:03
From: ts@uwasa.fi (Timo Salmi)

121. How to make %0 include the full path of the called batch file?
===================================================================

The problem with the %0 parameter indeed is that it does not include
the path unless explicitly given in the call. The path can, however,
be ensured:
  @echo off
  set prog_=%0
  echo %0|find /i ".bat">nul
  if errorlevel==1 set prog_=%0.bat
  echon set prog_=>tmp$$$.bat
  attrib %prog_%|cut 14 80|sed "s/ //g">>tmp$$$.bat
  call tmp$$$
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  echo prog_=%prog_%
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:03 2002
Subject: Recent files
Date: Fri, 1 Mar 2002 01:02:03
From: ts@uwasa.fi (Timo Salmi)

122. How can I test if a file is dated less than hour ago?
==========================================================

All the ingredients of this task are already present in this tricks
collection, so we'll just put the (complicated) code together. We'll
be using a number of my auxiliary programs, and we will be assuming
that the country-dependent date format uses "/" as the separator.
Note that TARGET.EXE ftp://garbo.uwasa.fi/pc/filefind/target15.zip
would not work here since it does not include the time (only the
date) option.
  @echo off
  ::
  rem The file to be checked
  if not "%1"=="" goto _exist
  echo Usage: %0 FileName
  goto _out
  :_exist
  if exist %1 goto _get
  echo File %1 not found
  goto _out
  ::
  rem Get today's date
  :_get
  rem>tmp$$$.dte
  echon set date_=>tmp$$$.bat
  dir tmp$$$.dte|find "TMP$$$   DTE"|cut 28 35>>tmp$$$.bat
  ::
  rem Get the current time
  echon set hh_=>>tmp$$$.bat
  dir tmp$$$.dte|find "TMP$$$   DTE"|cut 39 40>>tmp$$$.bat
  echon set mm_=>>tmp$$$.bat
  dir tmp$$$.dte|find "TMP$$$   DTE"|cut 42 43>>tmp$$$.bat
  call tmp$$$
  ::
  rem Convert to minutes
  fn /e %hh_%*60+%mm_% 0 > nul
  set min_=%fn_%
  ::
  rem The information of the file under observation
  dir %1|find "/">tmp$$$.dir        %Date format with / is assumed%
  echon set datef_=>tmp$$$.bat
  type tmp$$$.dir|cut 28 35>>tmp$$$.bat
  echon set hhf_=>>tmp$$$.bat
  type tmp$$$.dir|cut 39 40>>tmp$$$.bat
  echon set mmf_=>>tmp$$$.bat
  type tmp$$$.dir|cut 42 43>>tmp$$$.bat
  call tmp$$$
  ::
  rem Convert hours and minutes to minutes
  fn /e %hhf_%*60+%mmf_% 0 > nul
  set minf_=%fn_%
  ::
  rem Comparisons
  if "%datef_%"=="%date_%" goto _today
  echo File %1 is not dated today
  goto _clean
  ::
  :_today
  echo File %1 is dated today
  fn /e %minf_%-%min_%+60 0 > nul
  echo %fn_%|find "-" > nul
  if errorlevel==1 goto _recent
  echo File %1 is date more than an hour ago
  goto _clean
  :_recent
  echo File %1 is dated an hour or less ago
  ::
  :: Clean up
  :_clean
  for %%v in (date_ datef_ hh_ hhf_ mm_ mmf_ min_ minf_ fn_) do set %%v=
  for %%f in (bat dir dte) do if exist tmp$$$.%%f del tmp$$$.%%f
  ::
  :_out
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:04 2002
Subject: Screen colors
Date: Fri, 1 Mar 2002 01:02:04
From: ts@uwasa.fi (Timo Salmi)

123. How I can (re)set the screen text and background colors?
=============================================================

Take a look at the separate COLOR.BAT batch file in this FAQ
collection. It requires that you have ANSI.SYS (or a similar) screen
driver loaded. (Cf. item #105).
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:05 2002
Subject: Echo status
Date: Fri, 1 Mar 2002 01:02:05
From: ts@uwasa.fi (Timo Salmi)

124. How do I get the echo status into an environment variable?
===============================================================

By echoing the "echo" into a temporary file and testing the contents
of that file for "on". For example
  @echo off
  echo>%temp%\tmp.$$$
  type %temp%\tmp.$$$|find "on"
  if errorlevel==1 set echo_=OFF
  if not errorlevel==1 set echo_=ON
  for %%f in (%temp%\tmp.$$$) do if exist %%f del %%f
  echo The status of the echo is %echo_%
  set echo_=
As a sideline note that if the environment variable temp is not set,
it still works. The %temp%\tmp.$$$ file will be written to the root
directory of the current drive.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:06 2002
Subject: Delete empty dorectories
Date: Fri, 1 Mar 2002 01:02:06
From: ts@uwasa.fi (Timo Salmi)

125. How can I delete all empty directories on a drive?
=======================================================

This solution owes in parts to the ideas by several knowledgeable
batch authors on the Usenet news. Don't run it unless you actually
want to delete the directories. The auxiliary program SED.EXE is
needed.
  @echo off
  ::
  :: Handle the potential MS-DOS TEMP directory on the drive
  echo Ensure that there is something in there > marker.$$$
  if exist c:\temp\nul copy marker.$$$ c:\temp
  ::
  :: Build a list of directories of the target file
  :: Pipe through reverse sort to last handle subdirectories
  dir c:\*.* /a:d /s /b /l|sort /r> tmp$$$.dir
  ::
  :: Make and call a batch that removes all directories
  :: The crucial trick is that MS-DOS will only remove empty ones
  type tmp$$$.dir | sed -e "s/.*/rmdir &/" > tmp$$$.bat
  call tmp$$$
  ::
  :: Show what is left
  dir c:\*.* /a:d /s /b
  ::
  :: Clean up
  for %%f in (tmp$$$.dir tmp$$$.bat) do if exist %%f del %%f
  for %%f in (marker.$$$ c:\temp\marker.$$$) do if exist %%f del %%f
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:07 2002
Subject: Generalized environment set
Date: Fri, 1 Mar 2002 01:02:07
From: ts@uwasa.fi (Timo Salmi)

126. How can I put an = into an environment variable?
=====================================================

You can best use an auxiliary program to do this task. John's
utility, described below in a Garbo archives announcement, is one of
the options.

Sat 12-Jan-2002: Acquired to Garbo archives a neat little
environment setting utility from my net-friend Dr. John Stockton's
collection. It can include (for example in batches) an = in the
variable names (which MS-DOS COMMAND.COM can't do). For example, one
might make the following test
  @echo off
  echo.|stow test Timo=Salmi
  echo %test%
  set
The package is
 4619 Apr 26 2001 ftp://garbo.uwasa.fi/pc/envutil/stow.zip
 stow.zip General set parent environment variables, J.Stockton

For a brief note and response please see
http://groups.google.com/advanced_group_search
Message-ID: <a2k4qb$c8c@poiju.uwasa.fi>
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:08 2002
Subject: Reversing a line
Date: Fri, 1 Mar 2002 01:02:08
From: ts@uwasa.fi (Timo Salmi)

127. How can I reverse a word or an entire line?
================================================

A QBASIC solution to reverse a single word:
  @echo off
  ::
  :: Check the input
  if "%1"=="" goto _usage
  ::
  :: Build a QBASIC program to a file tmp$$$.bas
  >  tmp$$$.bas echo LET a$ = "%1"
  >> tmp$$$.bas echo FOR I = 1 TO LEN(a$)
  >> tmp$$$.bas echo   LET b$ = MID$(a$, i, 1) + b$
  >> tmp$$$.bas echo NEXT I
  >> tmp$$$.bas echo OPEN "tmp###.bat" FOR OUTPUT AS #1
  >> tmp$$$.bas echo PRINT #1, "@echo off"
  >> tmp$$$.bas echo PRINT #1, "set revers_=" ; b$
  >> tmp$$$.bas echo CLOSE #1
  >> tmp$$$.bas echo SYSTEM
  ::
  :: Run the QBASIC program
  qbasic /run tmp$$$.bas
  ::
  :: Run the batch that sets the environment variables
  call tmp###.bat
  ::
  :: Show (test) results
  echo %1
  echo %revers_%
  ::
  :: Clean up
  for %%f in (tmp$$$.bas tmp###.bat) do if exist %%f del %%f
  set revers_=
  goto _end
  ::
  :: Error messages
  :_usage
  echo Usage %0 String
  ::
  :: End
  :_end

A GAWK solution to reverse all the lines of a text file:
  @echo off
  ::
  echo This is the last test file line>test$$$.txt
  echo This is the second test file line>>test$$$.txt
  echo Line three>>test$$$.txt
  ::
  type test$$$.txt|gawk 'NR==1{n=length();for(i=1;i!=n+1;i++)printf"%%s",substr($0,n-i+1,1);printf"\n"}'
  ::
  for %%f in (test$$$.txt) do if exist %%f del %%f

What if you want only the second line reversed, but all the lines
printed? Use the following as the GAWK line. Note that the length of
the line becomes critical because of MS-DOS limitations.
  <test$$$.txt gawk '{if(NR==2){n=length();for(i=1;i!=n+1;i++)printf"%%s",substr($0,n-i+1,1);printf"\n"}else{printf"%%s\n",$0}}'

Incidentally, there is a way round the length limitation in using
GAWK in batches. First make the batch write a separate GAWK program
file. In that case we could have, for example
  @echo off
  ::
  echo This is the last test file line>test$$$.txt
  echo This is the second test file line>>test$$$.txt
  echo Line three>>test$$$.txt
  ::
  > tmp$$$.awk echo {if(NR==2)
  >>tmp$$$.awk echo {n=length()
  >>tmp$$$.awk echo for(i=1;i!=n+1;i++)
  >>tmp$$$.awk echo printf"%%s",substr($0,n-i+1,1);printf"\n"}
  >>tmp$$$.awk echo else{printf"%%s\n",$0}}
  ::
  type test$$$.txt|gawk -f tmp$$$.awk
  ::
  for %%f in (test$$$.txt tmp$$$.awk) do if exist %%f del %%f
In the above, note the deliberate mix of redirection techniques.
They have been chosen for demonstration.

The reversing can also be done using SED.EXE. John Savage posted the
following interesting solution on the Usenet news
  type test$$$.txt | sed "G; :o; s/\(.\)\(\n.*\)/\2\1/; to; s/.//"

This item was about reversing the contents of individual lines. What
about reversing the order of the lines of a file, instead of the
line contents. That question is covered in a later item #131.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:09 2002
Subject: Date and time into file
Date: Fri, 1 Mar 2002 01:02:09
From: ts@uwasa.fi (Timo Salmi)

128. How can I add date and time to each line of a text file?
=============================================================

The ingredients already are covered in this FAQ so here is the
solution outright:
  @echo off
  ::
  echo This is the last test file line>test$$$.txt
  echo This is the second test file line>>test$$$.txt
  echo.>>test$$$.txt
  echo Line four>>test$$$.txt
  ::
  rem Assume date format dd/mm/yyyy e.g. 10/02/2001
  echo.|date|gawk 'NR==1 {printf "%%s\n",$5}'>date$$$.tmp
  type date$$$.tmp|gawk -F/ '{printf "set yy_=%%s\n",substr($3,3,2)}'>tmp$$$.bat
  type date$$$.tmp|gawk -F/ '{printf "set mm_=%%s\n",$2}'>>tmp$$$.bat
  type date$$$.tmp|gawk -F/ '{printf "set dd_=%%s\n",$1}'>>tmp$$$.bat
  ::
  rem Assume time format 19:33:39,37
  echo.|time|gawk 'NR==1 {printf "%%s\n",$4}'>time$$$.tmp
  type time$$$.tmp|gawk -F: '{printf "set hr_=%%s\n",$1}'>> tmp$$$.bat
  type time$$$.tmp|gawk -F: '{printf "set mn_=%%s\n",$2}'>> tmp$$$.bat
  type time$$$.tmp|gawk -F: '{printf "set ss_=%%s\n",substr($3,1,2)}'>>tmp$$$.bat
  ::
  :: Call the batch that has been built to get the time and date
  call tmp$$$.bat
  ::
  :: Output the text file inserting the date and time
  type test$$$.txt|gawk '{printf "%yy_%%mm_%%dd_% %hr_%%mn_%%ss_% %%s\n",$0}'
  ::
  :: Clean up
  for %%f in (test$$$.txt tmp$$$.bat date$$$.tmp time$$$.tmp) do del %%f
  for %%v in (yy_ mm_ dd_ hr_ mn_ ss_) do set %%v=
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:10 2002
Subject: Complicated find selection
Date: Fri, 1 Mar 2002 01:02:10
From: ts@uwasa.fi (Timo Salmi)

129. Is a column-sensitive search possible through a text file?
===============================================================

This item is about the following kind of a question. A user has a
text file where the lines are about 500 characters long. He want to
print lines that have a "Yes" string at columns 305-307. The
complication is that the string may appear also elsewhere on a line
and those occurrences should not be taken into account, which meant
that a simple FIND will not solve the problem. The principle for
solving such a problem is shown in the simple test batch below where
the criterion is assumed be at 12-14.
  @echo off
  ::
  :: Demotext       12345678901234
  > tmp$$$.txt echo Line 1 No  Yes
  >>tmp$$$.txt echo Line 2 Yes No
  >>tmp$$$.txt echo Line 3 Yes Yes
  >>tmp$$$.txt echo Line 4 No  No
  ::
  :: The filter
  type tmp$$$.txt|gawk '{if(substr($0,12,3)~/Yes/)printf"%%s\n",$0}'
  ::
  :: Clean up
  for %%f in (tmp$$$.txt) do del %%f
What if you wish the test bo be case-independet. The relevant GAWK
statement then is
  gawk '{if(substr($0,12,3)~/[Yy][Ee][Ss]/)printf"%%s\n",$0}'
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:11 2002
Subject: How many words
Date: Fri, 1 Mar 2002 01:02:11
From: ts@uwasa.fi (Timo Salmi)

130. How can I calculate the number of words in a text file?
============================================================

Again, with GAWK
  @echo off
  type myfile.txt|gawk '{n+=NF}END{printf "@set words_=%%s\n",n}'>tmp$$$.bat
  for %%c in (call del) do %%c tmp$$$.bat
  echo Number of words = %words_%
  set words_=
See the earlier item #118 for an application of this information.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:12 2002
Subject: Reversing a file
Date: Fri, 1 Mar 2002 01:02:12
From: ts@uwasa.fi (Timo Salmi)

131. How can I put the lines of a text file into a reverse order?
=================================================================

The trick is to insert line numbers (covered also earlier), reverse
sorting the file, and then removing the line numbers. This is doable
with GAWK as follows. Note that you need a token that will not be
present anywhere in the original file. Below we assume that  is
such a token. You'll also need leading zeroes for getting right the
reverse sorting by the line numbers.
  @echo off
  >  tmp$$$.awk echo {p=length(NR)
  >> tmp$$$.awk echo lead="000"
  >> tmp$$$.awk echo lead=substr(lead,1,4-p)
  >> tmp$$$.awk echo printf"%%s%%s%%s\n",lead,NR,$0}
  ::
  type myfile.txt|gawk -f tmp$$$.awk>tmp$$$.txt
  type tmp$$$.txt|sort /r|gawk -F '{printf "%%s\n",$2}'
  ::
  for %%f in (tmp$$$.txt tmp$$$.awk) do if exist %%f del %%f

There are several good alternatives which you can find using
<http://groups.google.com/advanced_group_search> by entering
alt.msdos.batch as the newsgroup and as the subject give, including
the quotes: "Re: Reversing a file". They include, for example,
 awk "{Array[NR]=$0}END{for(x = NR;x!=0;x--)print Array[x]}"
from Ted Davis and
 sed -n "1!G;h;$p"
from an anonymous user.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:13 2002
Subject: Adding time
Date: Fri, 1 Mar 2002 01:02:13
From: ts@uwasa.fi (Timo Salmi)

132. How can I add 5:23:11 to the current time?
===============================================

This is getting awfully complicated and thus drifting beyond this
FAQ better into the realm of higher level programming. Let's
nevertheless have a rudimentary demonstration with GAWK. The
completion of the date carryover is left to the reader. Likewise,
putting the results into environment variables and utilizing the
results is left to the reader (there are plenty of examples of the
environment variable insertion throughout this FAQ).
  @echo off
  ::
  :: Usage
  if "%3"=="" goto _usage
  ::
  :: Get the current time. Assume time format 19:33:39,37
  echo.|time|gawk 'NR==1{printf "%%s\n",$4}'>time$$$.tmp
  type time$$$.tmp|gawk -F: '{printf "set hr_=%%s\n",$1}'>>tmp$$$.bat
  type time$$$.tmp|gawk -F: '{printf "set mn_=%%s\n",$2}'>>tmp$$$.bat
  type time$$$.tmp|gawk -F: '{printf "set ss_=%%s\n",substr($3,1,2)}'>>tmp$$$.bat
  ::
  :: Call the batch that has been built
  call tmp$$$.bat
  ::
  :: Show the results about the current time
  echo hr_=%hr_%
  echo mn_=%mn_%
  echo ss_=%ss_%
  ::
  :: Build a GAWK script for the additions
  >  tmp$$$.awk echo {
  >> tmp$$$.awk echo sec=(%ss_%+%3) %% 60
  >> tmp$$$.awk echo min=((%mn_%+%2)+int((%ss_%+%3)/60)) %% 60
  >> tmp$$$.awk echo hrs=(%hr_%+%1)+int(((%mn_%+%2)+int((%ss_%+%3)/60))/60)
  >> tmp$$$.awk echo days=int(hrs/24)
  >> tmp$$$.awk echo hrs=hrs %% 24
  >> tmp$$$.awk echo printf "days=%%s\n",days
  >> tmp$$$.awk echo printf "hrs=%%s\n",hrs
  >> tmp$$$.awk echo printf "min=%%s\n",min
  >> tmp$$$.awk echo printf "sec=%%s\n",sec
  >> tmp$$$.awk echo }
  ::
  :: Display the results of the time addition
  echo.|gawk -f tmp$$$.awk
  ::
  :: Clean up
  for %%v in (hr_ mn_ ss_) do set %%v=
  for %%f in (time$$$.tmp tmp$$$.bat tmp$$$.awk) do if exist %%f del %%f
  goto _out
  ::
  :_usage
  echo Usage: %0 HoursToAdd MinutesToAdd SecondsToAdd
  ::
  :_out
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:14 2002
Subject: Number of parameters
Date: Fri, 1 Mar 2002 01:02:14
From: ts@uwasa.fi (Timo Salmi)

133. How do I find out the number of parameters given to my batch?
==================================================================

The format of an MS-DOS batch call is
  MYBAT.BAT [Param1] [Param2] [...] [Param9]
Unlike in Unix there is no ARGC (number of command line arguments)
in straight MS-DOS batch programming. If you have nine parameters or
less, and have not used shift (= change the position of replaceable
parameters in a batch program), the you can find out the number of
parameters given to the batch by using
  echo.%1 %2 %3 %4 %5 %6 %7 %8 %9|gawk '{printf"ARGC=%%s\n",NF}'
or
  @echo off
  echo.%1 %2 %3 %4 %5 %6 %7 %8 %9|gawk '{printf"@set ARGC=%%s\n",NF}'>tmp$$$.bat
  call tmp$$$
  echo Number of replaced parameters = %ARGC%
  ::
  for %%f in (tmp$$$.bat) do if exist %%f del %%f
  for %%v in (ARGC) do set %%v=

If you wish to avoid using GAWK, the solution is
  @echo off
  ::
  set argc=0
  if not "%1"=="" set argc=1
  if not "%2"=="" set argc=2
  if not "%3"=="" set argc=3
  if not "%4"=="" set argc=4
  if not "%5"=="" set argc=5
  if not "%6"=="" set argc=6
  if not "%7"=="" set argc=7
  if not "%8"=="" set argc=8
  if not "%9"=="" set argc=9
  ::
  echo Number of replaced parameters is %argc%
  set argc=

If there are more than nine replaceable parameters, then things get
more complicated. The outline of a solution is the following:
  @echo off
  set argc=0
  :_loop
  shift
  echo %argc%|gawk '{printf"@set argc=%%s\n",$1+=1}'>tmp$$$.bat
  if not "%0"=="" call tmp$$$
  if not "%1"=="" goto _loop
  ::
  echo Number of replaced parameters was %argc%
  ::
  set argc=
  if exist tmp$$$.bat del tmp$$$.bat
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:15 2002
Subject: Removing colums
Date: Fri, 1 Mar 2002 01:02:15
From: ts@uwasa.fi (Timo Salmi)

134. How to remove the first two characters throughout my text file?
====================================================================

Removing the first two columns from the text file is the same thing
as including all the columns starting from column 3.
Using GnuAWK
  type myfile.txt|gawk '{printf"%%s\n",substr($0,3,255)}'
More generally
  type myfile.txt|gawk '{printf"%%s\n",substr($0,3)}'
Using CUT.EXE
  type myfile.txt|cut 3 255
The principle is easily extended to any set of columns.

What if you wish to leave out the first field in a text file. Say
that you have in myfile.txt
  Line 1 No  Yes
  Line 2 Yes No
  Line 3 Yes Yes
  Line 4 No  No
Then
  type myfile.txt|gawk '{for(i=2;i!=NF+1;i++)printf"%%s ",$i;printf"\n"}'
will leave out the first field. There is a catch, though. The
aligning will be lost.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:16 2002
Subject: Removing colums
Date: Fri, 1 Mar 2002 01:02:16
From: ts@uwasa.fi (Timo Salmi)

135. Can one calculate the difference between two times in a batch?
===================================================================

Yes, using G(nu)AWK scripting in a batch one can write for example
  @echo off
  ::
  if "%6"=="" goto _usage
  ::
  > tmp$$$.awk echo {
  >>tmp$$$.awk echo  d=60*60*%4+60*%5+%6-(60*60*%1+60*%2+%3)
  >>tmp$$$.awk echo  if (index(d,"-")) d=d+24*60*60
  >>tmp$$$.awk echo  h = int(d/3600)
  >>tmp$$$.awk echo  m = int(d/60)-60*h
  >>tmp$$$.awk echo  s = d-60*int(d/60)
  >>tmp$$$.awk echo  printf"hrs=%%s\n",h
  >>tmp$$$.awk echo  printf"min=%%s\n",m
  >>tmp$$$.awk echo  printf"sec=%%s\n",s
  >>tmp$$$.awk echo }
  ::
  echo.|gawk -f tmp$$$.awk
  ::
  :: Clean up
  for %%f in (tmp$$$.awk) do if exist %%f del %%f
  goto _out
  ::
  :_usage
  echo Usage: %0 BegHrs BegMin BegSec EndHrs EndMin EndSec
  ::
  :_out
Easy to adapt to put the values into environment variables, if need
be.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:17 2002
Subject: Debug safety
Date: Fri, 1 Mar 2002 01:02:17
From: ts@uwasa.fi (Timo Salmi)

136. Is it safe to use debug or other similar tools in batches?
===============================================================

It is a matter of opinion. The somewhat edited posting below
outlines what I personally think.

-From ts@UWasa.Fi Fri Feb 15 08:02:08 EET 2002
-From: ts@UWasa.Fi (Timo Salmi)
-Newsgroups: alt.msdos.batch
-Subject: Re: Debug Script
-Date: 15 Feb 2002 08:01:11 +0200
-Organization: University of Vaasa, Finland
-Message-ID: <a4i877$hlh@poiju.uwasa.fi>
-X-Warning: Junk / bulk email will be auto-filtered, traced and reported.
-X-Password: Spam foiling, elm filter will first return a required email password.

A word of caution.

With the frequent debug scripts, the gentle readers should be aware
that if you play around with the debug solutions long enough, you
are eventually bound to get your computer into knots. It is possible
to harm your data by running erroneous scripts, or by running
scripts written for another MS-DOS version or interpreter that you
are currently using.

In there is a constant, sometimes hidden, sometimes openly vocal tug
of war going on what utilities and methods are cosher in MS-DOS
programming. Third party utilities that run under the vanilla MS-DOS
are sometimes frowned at. But at least the well-proven standard ones
(such as e.g. sed and awk) are less liable to cause harm than the
more exotic tampering with your system's fundamentals and its
configuration.

Anyway. Whichever of the views one happens to hold vis a vis what to
use, the gentle readers should always bear in mind this before you
run anything on your computer: You always use the solutions posted
on the Usenet news (whatever the flavor of the solution) and in this
collection at your own risk. It always is your own call!

If one does not understand a batch solution, one's risk in increases
manifold. That is why I very seldom run any of the debug-involving
solutions on my own PC.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:18 2002
Subject: Keyword extraction
Date: Fri, 1 Mar 2002 01:02:18
From: ts@uwasa.fi (Timo Salmi)

137. How can I retrieve a keyword from a text file into environment?
====================================================================

Below is a demonstration of the outline to solve such problems.
Fairly easy to customize. Also see the somewhat similar item #118.
  @echo off
  ::
  :: Build a demonstration file
  > myfile.txt echo Line 1
  >>myfile.txt echo Line 2
  >>myfile.txt echo Keyword: This is the key
  >>myfile.txt echo Line 4
  ::
  :: Build an awk script which prepares a batch file
  :: that sets the keyword environment variable
  > tmp$$$.awk echo {
  >>tmp$$$.awk echo if($1~/Keyword:/)
  >>tmp$$$.awk echo {printf "@set keyword="
  >>tmp$$$.awk echo for (i=2;i!=NF+1;i++) printf"%%s ",$i
  >>tmp$$$.awk echo printf "\n"}
  >>tmp$$$.awk echo }
  ::
  :: Run the script and the batch it has made
  <myfile.txt gawk -f tmp$$$.awk|sed "s/ *$//">tmp$$$.bat
  call tmp$$$
  ::
  :: Show the results
  echo The keyword is: %keyword%
  ::
  :: Clean up
  for %%f in (tmp$$$.awk myfile.txt tmp$$$.bat) do del %%f
  set keyword=
In the above the if($1~/Keyword:/) means if $1 "contains". This
if($0~/Keyword:/) would get a similarly match for the entire line.
If you wish to test for an exact match, then use if($1=="Keyword:").
Test the various alternatives and you'll see the difference.
--------------------------------------------------------------------

From ts@uwasa.fi Fri Mar 1 01:02:19 2002
Subject: Finding duplicate files
Date: Fri, 1 Mar 2002 01:02:19
From: ts@uwasa.fi (Timo Salmi)

138. How to find if there is an identical file elsewhere on my HD?
==================================================================

This item stems from a problem I faced myself. I had an image file
(a jpg photograph actually) and wanted to find if I had a copy of it
somewhere else with a different name. The batch below extracts the
size, the date- and time-stamps of my file, then makes a directory
of the drives where I suspect that the duplicate might lie, and
makes a simple find comparison. This is a batch without undue
finesse, just locating such potential duplicate files. It does not
do any actual contents comparison (e.g. with FC) or other operations
on the file(s) found. Before running the batch, substitute the path
to GAWK with yours (I have mine at F:\FTOOLS outside the default
path). Also note the SAMENAME.BAT and SAMESIZE.BAT files in the
collection.

  @echo off
  ::
  :: Preliminaries
  if "%1"=="" goto _usage
  if "%1"=="Yes" goto _allindir
  if not exist %1 goto _notfound
  ::
  :: Get the file size and the file date/time
  dir %1|find "/">tmp$$$.dir
  type tmp$$$.dir
  ::
  :: Build a G(nu)AWK script to get the file's size, date and time
  :: Take care of the possibility of no extension in the file name
  > tmp$$$.awk echo {
  >>tmp$$$.awk echo if(substr($0,10,3)=="   ")i=1
  >>tmp$$$.awk echo printf"@set fsize_=%%s\n",$(3-i)
  >>tmp$$$.awk echo printf"@set fdate_=%%s\n",$(4-i)
  >>tmp$$$.awk echo printf"@set ftime_=%%s\n",$(5-i)
  >>tmp$$$.awk echo }
  ::
  :: Run the script and the batch it has made
  <tmp$$$.dir f:\ftools\gawk -f tmp$$$.awk>tmp$$$.bat
  call tmp$$$.bat
  ::
  :: Make a directory of the target files
  :: Customize as appropriate for your own search
  dir /s g:\*.*>tmp$$$.dir
  dir /s h:\*.*>>tmp$$$.dir
  ::
  :: Show files of same same size date, and time
  find "%fsize_%" tmp$$$.dir|find "%fdate_%"|find "%ftime_%"
  ::
  :: Clean up
  for %%f in (dir awk bat) do if exist tmp$$$.%%f del tmp$$$.%%f
  for %%v in (fsize fdate ftime) do set %%v_=
  goto _out
  ::
  :_usage
  echo Usage: %0 [Filename or Yes_If_For_All_In_Current_Directory]
  goto _out
  ::
  :_notfound
  echo File %1 not found, or is a directory
  goto _out
  ::
  :_allindir
  :: The option of checking all the files in the default directory
  dir /b *.*|f:\ftools\gawk '{printf"call %0 %%s\n",$0}'>tmp$$$2.bat
  call tmp$$$2
  for %%f in (tmp$$$2.bat) do if exist %%f del %%f
  ::
  :_out
--------------------------------------------------------------------
