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