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.

1
  • Would my answer be more acceptable if I compiled the code and provided a link to the executable? I can do that if you'd like, if you don't have access to a compiler or don't want to do it yourself.
    – BenjiWiebe
    Jun 12, 2014 at 2:41

6 Answers 6

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;
}
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"

9
  • 1
    I'm still looking for a way to do this without a temporary file within the basic (non-PowerShell) command line, if possible.
    – Bob
    Jun 12, 2014 at 1:39
  • On *nix, I always tee to stderr; is there a way to tee to both stderr and stdout in PowerShell, or does Windows/PowerShell not have that concept?
    – BenjiWiebe
    Jun 12, 2014 at 2:03
  • @BenjiWiebe /dev/stderr, correct? That doesn't exist on Windows, as far as I know. Something with named pipes would be possible, but I'm trying to avoid writing a program just for this purpose!
    – Bob
    Jun 12, 2014 at 2:14
  • 1
    Programmatically speaking, /dev/stderr is a kernel concept that redirects stuff to the file descriptor for stderr. Would there not be a way in Windows to redirect to stderr (NOT /dev/stderr)?
    – BenjiWiebe
    Jun 12, 2014 at 2:16
  • See my answer for an explanation of what I was thinking (and it MIGHT WORK!)
    – BenjiWiebe
    Jun 12, 2014 at 2:20
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"
}
3
  • That's exactly what I did, and what I described in my self-answer... Does this answer add any more information?
    – Bob
    Jun 12, 2014 at 22:26
  • Ah, I see, the process is slightly different (doesn't use a Tee-Object directly, instead manually prints the output later). Then I suppose one of the disadvantages of this approach is it has to capture the entire output and then displays it as one go - which means the user will not see any output while the process is running, only after it ends. Its acceptable for short-running tasks, but not good at all for longer ones.
    – Bob
    Jun 12, 2014 at 22:33
  • Edited to simply output each line and check at the same time, setting a flag if it finds your message. As it stands your command being executed based on the message would not execute until the output is complete, which your question does state it cannot run asynchronously. However if you could use Start-Job in PowerShell to execute it outside of the current process if you need it to, then once the output is done check the status of the job with Get-Job.
    – user94184
    Jun 13, 2014 at 3:47
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.

4
  • Unfortunately, this is far past overkill for my particular task. If I wanted to use a POSIX emulation layer, I'd just use MSYS or cygwin. Also, tee itself doesn't help - it's the ability to redirect to the tty or output device as if it were a file, which is something POSIX provides, not the tee command.
    – Bob
    Jun 12, 2014 at 2:35
  • TCC can do just that: 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. Sorry you feel it's overkill: I would not be without TCC.
    – AFH
    Jun 12, 2014 at 10:15
  • Right - it would be great if you could include that in your answer (it's not immediately obvious that it provides some way to reference the console). Still overkill for my case, since I'm trying to create a build script to be committed to version control for other people to use, so I'd like to minimise third party software I'd need to bundle (if the licence even allows redis). A small binary like Benji's answer is acceptable, an entire command interpreter I'd need to install is too much. It could be useful for other people, though.
    – Bob
    Jun 12, 2014 at 11:44
  • Good point. I have updated my answer as you suggest, and I can see that Benji's solution could be a good fit for your specific need, but I thought I would test it, and I found that you need to add fflush(NULL); after the fprintf() line, in order to see the output in real-time. If you are in a commercial environment, you cannot use TCC without buying appropriate licences.
    – AFH
    Jun 12, 2014 at 12:37
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 ] [ ]

1
  • Yes, that is what I ended up doing, with the process described in a self-answer. Does this answer add any more information?
    – Bob
    Jun 12, 2014 at 22:27
-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 ' '.

2
  • 1
    If applied to arbitrary problems, this would execute the command twice, first time printing the output on the console, second time piping to the target. That might not work, e.g. if the command itself changes the state of files etc. on hard disk. Aug 1, 2014 at 21:42
  • @ThomasW, it will not execute the command twice. As you can see, first echo %1 will just print %1 parameter on screen, and second echo will send %1 parameter to %2 program. Of course it has some restrictions, but generally solves the problem.
    – Jet
    Aug 4, 2014 at 19:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .