4

I have a process I need to run within a batch file. This process produces some output. I need to both display this output to the screen and send (pipe) it to another program.

The bash method uses tee:

echo 'ee' | tee /dev/tty | foo

Is there an equivalent for Windows? I am happy to use PowerShell if necessary.

There are tee ports for Windows, but there does not appear to be an equivalent for /dev/tty, which complicates matters.


The specific use-case here: I have a program (launch4j) that I need to run, displaying output to the user. At the same time, I need to be able to detect success or failure in the script. Unfortunately, this program does not set an exit code, and I cannot force it to do so. My current workaround involves piping to find, to search the output (launch4j config.xml | find "Successfully created") - however, that swallows the output I need to display. Therefore, I need some way to both display to the screen and send the ouput to a command - and this command should be able to set ERRORLEVEL (it cannot run asynchronously). This will be used in a build script, which could be run on many different machines.

For this particular case, something lightweight is required - I cannot install additional frameworks or interpreters (e.g. perl as suggested in this answer). Also, any commercial programs must have a licence that allows redistribution.

Bob
  • 63,170

6 Answers6

1

One somewhat messy way I can think of is to use one of the ported tee programs, save to a temporary file and then test the file with find. However, the use of a temporary file may be undesirable.

If PowerShell is an option, it actually has a Tee-Output cmdlet. It's not quite as direct as the bash example, but it does have a -Variable option to save the output into a variable, which can then be searched:

# save result in $LastOutput and also display it to the console
echo "some text" | Tee-Output -Variable LastOutput

# search $LastOutput for a pattern, using Select-String
# instead of find to keep it within PowerShell
$Result = $LastOutput | Select-String -Quiet "text to find"

# $Result should contain either true or false now
# this is the equivalent of batch "if errorlevel 1"
if ($Result -eq $True) {
    # the string exists in the output
}

To answer the more general question, it is also possible to pipe the variable into any other program, which will then set $LastExitCode. As a one-liner that can be called from the basic command line: powershell -c "echo text | Tee-Object -Variable Result; $Result | foo"

Bob
  • 63,170
1

You could try compiling this code and using it like: echo something | mytee | foo.
I don't know if it will work, since I don't know how Windows deals with stderr/stdout, but it might work.

#include <stdio.h>
int main()
{
    int c;
    while((c = fgetc(stdin)) != EOF)
    {
        printf("%c", c);
        fprintf(stderr, "%c", c);
    }
    return 0;
}
BenjiWiebe
  • 9,173
1

Why not just execute the command in PowerShell using Invoke-Command and capture the results into a variable. You search the variable for your results if they are there, do something, and then display all the output to the console.

Test file to capture output from is just notepad with the following text (C;\Temp\OutputTest.txt):

blahlbalsdfh
abalkshdiohf32iosknfsda
afjifwj93f2ji23fnsfaijfafds
fwjifej9f023f90f3nisfadlfasd
fwjf9e2902fjf3jifdsfajofsda
jfioewjf0990f
Successfully Created
fsjfd9waf09jf329j0f3wjf90awfjw0afwua9

In your case though you would call your command as something like {& "Launch4j" config.xml} I believe, but for my example:

Invoke-Command -ScriptBlock {Get-Content C:\temp\OutputTest.txt} | foreach {
$_;
if ($_ -match "successfully created") {$flag = $true}
}
if ($flag) {
"do whatever"
}
0

I would recommend TCC/LE from JP Software: it implements many of the features of bash, including TEE, in CMD-compatible syntax. In your example it would be:

echo ee|tee con:|foo

I tested with the following command

for /l %n in (1,1,10) do ( echo %n %+ delay)|tee con:|nl.

Here NL is a program which gives a numbered listing, and the output was interspersed unnumbered and numbered lines. The 1 second delay allowed me to see that both the console and the pipe reader were receiving lines simultaneously.

The LE version is free for private use.

AFH
  • 17,958
0

You could rewrite your batch file as a PowerShell script. PowerShell has Tee-Object (aliased as Tee).

Parameter Set: File Tee-Object [-FilePath] [-Append] [-InputObject ] [ ]

Parameter Set: LiteralFile Tee-Object -LiteralPath [-InputObject ] [ ]

Parameter Set: Variable Tee-Object -Variable [-InputObject ] [ ]

Keltari
  • 75,447
-1

Simply put this on a batch file (lets say echox.bat):

@echo off
echo %1
echo %1|%2

Then call that batch like this:

echox 'c:\' dir

It will print out c:\ and print output of dir c:\.

NOTE: as we are using %1, %2, parameters must be passed without spaces or within " " and ' '.

Jet
  • 2,576