<http://www.netikka.net/tsneti/info/tscmd094.htm>
Copyright © 2003-2010 by Prof. Timo Salmi  
Last modified Sat 4-Sep-2010 19:34:45

 
Assorted NT/2000/XP/.. CMD.EXE Script Tricks
From the html version of the tscmd.zip 1cmdfaq.txt file
To the Description and the Index
 

This page is edited from the 1cmdfaq.txt faq-file contained in my tscmd.zip command line interface (CLI) collection. That zipped file has much additional material, including a number of detached .cmd script files. It is recommended that you also get the zipped version as a companion.

Please see "The Description and the Index page" for the conditions of usage and other such information.



94} Can I send the script's output both to the screen and a log file?

One option is to use the UNIX-like tee.exe if you can find one on the net.

To show an example apply that on the CMDFAQ.CMD script containing
  @echo off & setlocal enableextensions
  if not [%1]==[] echo %1
  if not [%1]==[] echo %~1
  if not "%1"=="" echo %1
  endlocal & goto :EOF

The output will be
  C:\_D\TEST>cmdfaq "a b" | tee MyLog.txt
  "a b"
  a b
  b""=="" was unexpected at this time.

  C:\_D\TEST>type MyLog.txt
  "a b"
  a b
The error message goes to stdout only.

Another option is to use any suitable screen capture program.

A third one is to write a VBS cscript to do the "tee". Consider the following example script
  @echo off & setlocal enableextensions
  rem MYTEST.CMD
  echo Test row 1
  echo Test row 2
  endlocal & goto :EOF
Make and store the following TEE.VBS file at your path
  Dim str
  Do While Not WScript.StdIn.AtEndOfStream
    str = WScript.StdIn.ReadLine
    WScript.StdOut.WriteLine str
    WScript.StdErr.WriteLine str
  Loop

Then on the command line call
  MYTEST | cscript /nologo TEE.VBS > MYLOG.TXT

The output on the screen will be
  C:\_M>MYTEST | cscript /nologo TEE.VBS > MYLOG.TXT
  Test row 1
  Test row 2

And you can ascertain
  C:\_M>type mylog.txt
  Test row 1
  Test row 2

Also gawk could be used. For example call the following script
  @echo off & setlocal enableextensions
  echo Test row 1
  echo Test row 2
  endlocal & goto :EOF
from the command line using
  CMDFAQ|gawk "{print; print>\"c:\\_m\\out.txt\"}"
The output will be
  C:\_D\TEST>C:\_D\BAS\CMDFAQ.CMD|gawk "{print; print>\"c:\\_m\\out.txt\"}"
  Test row 1
  Test row 2
and the file c:\_m\out.txt will also contain
  Test row 1
  Test row 2

One could, of course, write "bare-batch" subroutines for selective "teeing". For example
  @echo off & setlocal enableextensions
  rem MYTEST.CMD
  set MyLogfile=C:\_M\mylog.txt
  if exist "%MyLogfile%" del "%MyLogfile%"
  call :teeEcho "Test row ^1"
  call :teeEcho "Test row ^2"
  call :teeDir "C:\_D\TEST\*.*"
  endlocal & goto :EOF
  ::
  :teeEcho
  setlocal
  set row_=%~1
  echo %row_%
  echo %row_%>>"%MyLogfile%"
  endlocal & goto :EOF
  ::
  :teeDir
  setlocal
  set row_=%~1
  dir %row_%
  dir %row_%>>"%MyLogfile%"
  endlocal & goto :EOF
The situation is, however, far from as simple as you might think. In the call :teeEcho the quoting is needed to pass on the entire argument. (Also note employing %~1 rather than %*). Most importantly, and subtly, the 1 and 2 need be escaped with the caret ^ lest they are (mis)taken for file handles! Else the output will not be what you except. There is, however, a better way round the problem:
  @echo off & setlocal enableextensions
  rem MYTEST.CMD
  set MyLogfile=C:\_M\mylog.txt
  if exist "%MyLogfile%" del "%MyLogfile%"
  call :teeEcho Test row 1
  call :teeEcho Test row 2
  call :teeDir "C:\_D\TEST\*.*"
  endlocal & goto :EOF
  ::
  :teeEcho
  setlocal
  echo %*
  >>"%MyLogfile%" echo %*
  endlocal & goto :EOF
  ::
  :teeDir
  setlocal
  dir %*
  >>"%MyLogfile%" dir %*
  endlocal & goto :EOF
Unfortunately, even that (better) solution breaks down with some poison characters in the passed text like & the ampersand. The batch-subroutine method of teeing is subject to pitfalls. However, the following is more robust.
  @echo off & setlocal enableextensions
  rem MYTEST.CMD
  set MyLogfile=C:\_M\mylog.txt
  if exist "%MyLogfile%" del "%MyLogfile%"
  call :teeEcho "Test row 1"
  call :teeEcho "Test row 2"
  endlocal & goto :EOF
  ::
  :teeEcho
  setlocal
  for /f "delims=" %%a in ("%~1") do echo %%a
  for /f "delims=" %%a in ("%~1") do >>"%MyLogfile%" echo %%a
  endlocal & goto :EOF

Consider a similar question. How does one always direct the header output of a script to the console (screen), but the body optionally either to the screen or a file? As an example search through a set of CMD and BAT files to identify which ones include the string "%ram%", the quotes inclusive.
  @echo off & setlocal enableextensions
  (
    echo +----------------------------------------------------+
    echo ^| Search a list of BAT and CMD files for a string    ^|
    echo ^| By Prof. Timo Salmi, Last modified Sat 18-Apr-2009 ^|
    echo +----------------------------------------------------+
  )>con
  set SearchString=\"%%ram%%\"
  findstr /s /m /c:"%SearchString%" "C:\_E\*.BAT"
  findstr /s /m /c:"%SearchString%" "C:\_E\*.CMD"
  findstr /s /m /c:"%SearchString%" "C:\_F\*.BAT"
  findstr /s /m /c:"%SearchString%" "C:\_F\*.CMD"
  endlocal & goto :EOF

Normally, the screen output would be something like
  C:\_M>C:\_D\TEST\CMDFAQ.CMD
  +----------------------------------------------------+
  | Search a list of BAT and CMD files for a string    |
  | By Prof. Timo Salmi, Last modified Sat 18-Apr-2009 |
  +----------------------------------------------------+
  C:\_E\ARCZIP\HOME.BAT
  C:\_E\ARCZIP\PHREN.BAT
  :
  C:\_F\XTOOLS\CLRM.CMD
  C:\_F\XTOOLS\CMDBOX.CMD

However
  C:\_M>C:\_D\TEST\CMDFAQ.CMD > "MyLog.txt"
  +----------------------------------------------------+
  | Search a list of BAT and CMD files for a string    |
  | By Prof. Timo Salmi, Last modified Sat 18-Apr-2009 |
  +----------------------------------------------------+
with all the rest of the output sent to MyLog.txt

References/Comments: (If a Google message link fails try the links within the brackets.)
  Google Groups May 1 2005, 10:11 am [M]
  Google Groups Jul 8 2006, 4:46 pm [M]
  Google Groups Jul 8 2006, 11:45 pm [M]
  Google Groups Sep 3 2010, 6:40 pm [M]
  Google Groups Sep 3 2010, 10:22 pm [M]
  Google Groups Sep 4 2010, 6:59 pm [M]