2

I am trying to make a Windows batch file (the main script) redirect all of it's output to a file while still displaying it on the screen. I cannot change all the code that calls the main script - I need to change the main script itself.

I have a solution that requires "bootstrapping" the main script by calling it again from another bat file (tee_start.bat). What I'm sure I want to avoid is refactoring all the other scripts that call main.bat to use the tee command - it will be a much smaller, safer change if I can put some new code in main.bat.

Is there a way to do this that does not involve restarting the main file as I am doing below? For example, is there a cmd.exe or powershell.exe command that says "Take my current STDOUT and tee it" - or does tee somehow support this behavior and I missed it? Or, alternatively, should I settle for what I have implemented as the least invasive method?

I have implemented a solution as follows:

main.bat

REM this is an example of bootstrap mode - it will restart this main script with all output logged
call tee_start.bat %~dpnx0 %*

echo This output should appear both in the screen console and in a log file

tee_start.bat

REM in order to bootstrap the output redirection - this script gets called twice - once to initialize logging with powershell tee command
REM second time, it just echoes log information and returns without doing anything
if x%LOG_FILE% neq x (
    echo Bootstrapped Process: %1%
    echo Escaped Arguments: %ADDL_ARGS%
    echo Log File: %LOG_FILE%
    goto :EOF
)

set ADDL_ARGS=
:loop_start
shift
if x%1 == x goto :after_loop
REM preserve argument structure (quoted or not) from the input
set ADDL_ARGS=%ADDL_ARGS% \"%~1\"
goto loop_start
:after_loop

SET LOG_FILE=/path/to/some/logfile.log

powershell "%1% %ADDL_ARGS% 2>&1 | tee -Append %LOG_FILE%"

1 Answer 1

4

I suggest the following approach, which makes do without the aux. tee_start.bat file:

main.bat content (outputs to log.txt in the current folder; adjust as needed):

@echo off & setlocal

if defined LOGFILE goto :DO_IT

    set "LOGFILE=log.txt"
    :: Reinvoke with teeing
    "%~f0" %* 2>&1 | powershell -NoProfile -Command "$input | Tee-Object -FilePath \"%LOGFILE%\"; \"[output captured in: %LOGFILE%]\""
    exit /b


:DO_IT

:: Sample output
echo [args: %*]
echo to stdout
echo to stderr >&2
echo [done]
  • Tee-Object uses a fixed character encoding, which is notably "Unicode" (UTF-16LE) in Windows PowerShell (powershell.exe), and (BOM-less UTF-8) in PowerShell (Core) 7+ (pwsh.exe).
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks @mklement0 - I'm studying your answer. "main" is long running (triggering sql scripts and moving files) and we do want to be able to view the log while it's running so I'll test that out. +1 for identifying the UTF-16 -> UTF-8 issue I was having without being prompted!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.