How can I both pipe and display output in Windows' command line?

8,856

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

Solution 2

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"

Solution 3

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"
}
Share:
8,856

Related videos on Youtube

tvdo
Author by

tvdo

Just a random fox passing through. You have backups, right? backup-brigade

Updated on September 18, 2022

Comments

  • tvdo
    tvdo almost 2 years

    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.

    • BenjiWiebe
      BenjiWiebe about 10 years
      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.
  • tvdo
    tvdo about 10 years
    I'm still looking for a way to do this without a temporary file within the basic (non-PowerShell) command line, if possible.
  • BenjiWiebe
    BenjiWiebe about 10 years
    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?
  • tvdo
    tvdo about 10 years
    @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!
  • BenjiWiebe
    BenjiWiebe about 10 years
    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
    BenjiWiebe about 10 years
    See my answer for an explanation of what I was thinking (and it MIGHT WORK!)
  • tvdo
    tvdo about 10 years
    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.
  • tvdo
    tvdo about 10 years
    @BenjiWiebe Ah, yea, what I meant is there's no direct equivalent in Windows that exposes file descriptors as device files. Writing a program that just prints to those would work, yes.
  • BenjiWiebe
    BenjiWiebe about 10 years
    Do you mean to say my code would actually work!? :)
  • tvdo
    tvdo about 10 years
    @BenjiWiebe Yes, it looks like it should work. I'm just going to use my PowerShell script for now, since that saves me having to add an extra binary, but have an upvote for a good solution :)
  • AFH
    AFH about 10 years
    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.
  • tvdo
    tvdo about 10 years
    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.
  • AFH
    AFH about 10 years
    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.
  • tvdo
    tvdo about 10 years
    That's exactly what I did, and what I described in my self-answer... Does this answer add any more information?
  • tvdo
    tvdo about 10 years
    Yes, that is what I ended up doing, with the process described in a self-answer. Does this answer add any more information?
  • tvdo
    tvdo about 10 years
    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.
  • Admin
    Admin about 10 years
    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.
  • Eris
    Eris almost 10 years
    In powershell you can do redirection *>&1 will redirect all output to 'stdout'
  • Thomas Weller
    Thomas Weller almost 10 years
    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.