ForEachFile.exe
v1.8




Usage: ForEachFile <FileSpecs> [Options] <Command>

ForEachFile executes Command once for each file that matches Filespec. Filespec can contain wildcard characters, attribute specifiers, multiple space-separated file names and/or file specs, etc. ForEachFile is a Win32 console app, and so supports long file names, long command lines, UNC names for network servers, and has no practical memory limits. It runs only under 32-bit versions of Windows.

Why it is Useful

Any time you have to do the same thing to more than one file, ForEachFile is potentially useful. It lets you write more powerful batch files with less effort. And it makes programs you use every day (like notepad) more useful by effectively enhancing their command-line support.

FileSpecs

Any file specification that is legal for DIR is legal as a ForEachFile filespec. This includes, of course, basic wildcards:
    *.*     all files
    *.txt   all files ending with .txt
    some*.* all files starting with "some"
    some.t?p    matches "some.tmp", "some.top", etc.
    
It includes multiple filespecs, like this:
    "*.txt c:\*.bat c:\bin\*.exe"
    
It also includes DIR-style attribute specifications:
    "/ad *.*"    match all Directories
    "/ar *.*"    match all Read-only files
    "/ah *.*"    match all Hidden files
    "/aa *.*"    match all files ready for Archiving
    "/as *.*"    match all System files
    
... including negation (-) and combination of attributes:
    "/ahs *.*"    match only files that are hidden and system
    "/a-d *.*"    match only files that are NOT directories
    "/a-r-d *.*"  match only files that are NOT readonly and NOT directories
       ... etc.
    
It includes two extended filespec qualifiers:
    /size<[eq][gte][lte][gt][lt]>([num][filename])
    /time<[eq][gte][lte][gt][lt]>([date][filename])
    
For example:
    "/sizeeq(0) *.*"         match all zero-length files
    "/sizegt(tmp.txt) *.*"   match all files bigger than tmp.txt
    "/timelt(tmp.txt) *.*"   match all files older than tmp.txt
    "/timeeq(c:\bin\$n) *.*" match all files with same-named and
                             same-timestamped copies in c:\bin.

    "/timegt(1997-02-28 17:30:45) *.*"  match all files that have
                                        been modified since 22 Feb
                                        1997 at 5:30 p.m.
    
And you can use all of these forms together, like this:
    "/a-r /sizegt(0) *.txt c:\*.bat c:\bin\*.exe"
    
When ForEachFile sees an "at" sign (i.e., '@') on the command line, it checks to see if the string immediately following it is the name of an existing file, and if so, replaces the "@filename" with the complete contents of the file. ForEachFile then interprets each line of the file as a separate argument.

Options

         -s:  recurse into subdirectories.
   -S<path>:  search along the specified list of directories.
         -n:  noshell (suppresses use of shell specified by COMSPEC)
         -a:  expand file timestamps using last-access times.
         -c:  chdir to $p before executing Command.
         -e:  echo Command to console before executing it.
         -p:  pause for confirmation before executing Command.
         -8:  use DOS (8.3) filenames in macro expansions.
    -nocrlf:  prevents internal echo from appending CR/LF.
 -lowercase:  forces use of lowercase in macro expansions.
-DepthFirst:  forces directories to be searched first when used with -s.
  -xo<file>:  redirects output to  command by command.
   -t<char>:  designate something other than '#' to start character codes.
   -m<char>:  designate a character other than '$' to start macros.
   -b<char>:  designate something other than ';' to break Command into parts.
   -f<spec>:  include files matching spec in Filespec.
   -x<spec>:  exclude files matching spec from Filespec.
    -shell:<shellstr>:  designate a shell other than that specified by COMSPEC.
    

Command

Any command that you can enter at the NT or Win95 command line is a legal ForEachFile command. Command normally includes some combination of file-reference macros (see below), and can include multiple semi-colon-separated commands, like this:
     echo About to delete "$n"; delete $n; if exist $n echo Failed to delete "$n".
    
By default, Command is executed through the shell specified by the COMSPEC environment variable, which means that certain operations which are built into COMMAND.COM, CMD.EXE, etc., will also work in a ForEachFile Command. These include conditional operations (e.g., "if exists ...", "if not errorlevel 1 ...", etc.), expansion of references to environment variable names (e.g., "if %cwd% == c:\source\db ..."), echo, copy, type, etc. If you prevent use of the shell with the -n option, commands will be executed more quickly, but "echo" will be the only built-in operation you can use (ForEachFile provides its own support for the echo command).

Macros

Given a matching file named D:\USR\DEFAULT\SOMETHNG.TXT and a search starting in D:\USR, macros in Command expand as follows:
      $f  D:\USR\DEFAULT\SOMETHING.TXT
      $n  SOMETHING.TXT
      $b  SOMETHING
      $e  .TXT
      $p  D:\USR\DEFAULT\
      $d  \USR\DEFAULT\
      $r  DEFAULT\  (path relative to current directory)
      $v  D:
      $S  size of D:\USR\DEFAULT\SOMETHING.TXT
      $T  timestamp of D:\USR\DEFAULT\SOMETHING.TXT
      $D  day part of D:\USR\DEFAULT\SOMETHING.TXT's date
      $M  month part of D:\USR\DEFAULT\SOMETHING.TXT's date
      $Y  year part of D:\USR\DEFAULT\SOMETHING.TXT's date

      #<000..255>  Expands to a single character designated by number.
    
Provided you aren't suppressing use of the shell with the -n option, you can refer to the values of environment variables by enclosing their names with percent signs. ForEachFile also supports three psuedo environment variables:
      %cwd%   Current Working Directory
      %date%  current date
      %time%  current time
    

Press 'q' at any time to abort cleanly AFTER execution of current command.

Return Codes

ForEachFile returns 2 if no files match FileSpec. Otherwise it returns an accumulation (a bitwise-OR) of the return codes of all the commands it executes. Note that under Win95, the return code is more meaningful if you use the -n switch to suppress use of the COMMAND.COM shell, because COMMAND.COM does not pass through the exit code of the last program it executes; this is not a problem under NT.



Examples


ForEachFile *.c -s fc $n j:$r$n
For each file with a .c extension in the current directory and all its subdirectories, this command runs the Win95 FC.EXE utility to compare the file with a file of the same name and in the same relative location on drive J:. This is handy because, although FC supports wildcard filespecs, it does not recurse into subdirectories.
ForEachFile "/timelt(\\SomeServer\sharename\src\$n) *.c *.h *.cpp" echo Local copy of $n is out of date.
This command lists all .c, .h, and .cpp files in the current directory whose timestamps are older than those of same-named copies on a server specified with a UNC name.


ForEachFile "/ar-d *.?" -s fc $f j:$d$f
This command searches for read-only non-directory files with single-character extensions in or under the current directory and uses FC.EXE to compare them with same-named files in a parallel location on drive J:. Note that the "/ar-d" is part of the filespec, not a ForEachFile switch.
ForEachFile *.txt -s start notepad $f
This command loads each .txt file in the current directory or any of its subdirectories into a separate instance of the Win95 notepad applet.


ForEachFile *.txt -s copy $f c:\temp\$b.bak
This command copies all .txt files in or under the current directory to the c:\temp directory and changes their extensions to .bak.


@ForEachFile "%1 %1.bat %1.exe %1.com" -S.;%path% -n echo $Y-$M-$D $T $S  $f
This command does the same thing as the UNIX "which" command -- it searches along the system's search path to determine which program is really being executed when a command is run.


ForEachFile "*.txt *.lst" -s -8 list $r$n
The -s (recurse into subdirectories) and -8 (use 8.3 filenames) arguments in this command make the shareware LIST utility more useful by solving its inability to handle long file and directory names, as well as its lack of support for directory recursion.


ForEachFile "*.tmp *.bak tmp*.*" -s -e move $f c:\temp
This command searches the current directory and all its subdirectories for files ending in .tmp or .bak, and for files beginning with "tmp", and moves them to c:\temp. The -e argument causes it to echo each command as it is executed so you can see what is happening.


ForEachFile "/ad *.?" -s echo $f
This command recurses through all subdirectories of the current directory and displays their names.


ForEachFile "/sizeeq(0) *.sbr" -s del $r$b.obj
This command locates all zero-length .sbr files in or under the current directory and deletes the corresponding .obj file.


ForEachFile "/timegt(MY.EXE) *.c" echo $n is newer than MY.EXE.
This command displays the names of all .c files in the current directory that are newer than the file MY.EXE.


Distribution

You can copy ForEachFile freely, and you can use it for any purpose, but you may not modify it. Click here to download it (it's about 89K):

To verify the authenticity of your copy, please check its CRC-32 value with your favorite .zip utility. You should see something like the following:

  Length  Method    Size  Ratio    Date    Time    CRC-32   Attr    Name
  ------  ------    ----- -----    ----    ----   --------  ----    ----
  188416 DeflatN    89231 52.7% 12-19-2001  5:08p 9db6374b --w----  ForEachFile.exe
ForEachFile is updated occasionally in http://www.users.qwest.net/~brecheencolemars.
Written by Cole Brecheen (cbrecheen@qwest.net).

Cole Brecheen, 411 North Holman, Portland, OR 97217-2141. (email: cbrecheen@qwest.net)
Last updated: 12-19-2001.