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

07
2014-07
  • Bob

    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.

  • Answers
  • BenjiWiebe

    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;
    }
    
  • Bob

    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"

  • AFH

    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.

  • Keltari

    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 ] [ ]

  • Shawn Melton

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

  • Related Question

    How do I install commands on command line? - Windows 7
  • RadiantHex

    Hi I often use Power Shell on Windows 7

    I'm often typing commands like

    C:\Python26\ python.exe example.py

    C:\Python26\scripts\ pip.exe install examplePy

    C:\Python26\scripts\ easy_install.exe examplePy

    I would like to simply type (as I do on Linux)

    python example.py

    easy_install examplePy

    pip install examplePy

    How do I go on about it?

    Help would be very much appreciated.


  • Related Answers
  • 8088

    You'll need to set the PATH. There are several paths, and in the end they (usually) get merged in to one.

    PATH entries are delimited by semicolon (;).

    Two are managed by Windows:

    • The system path. Affects all users on the computer.

    • The user path. Affects all programs you run under your user account.

    Under Windows 7, the first two are easy to find: open the start menu and type PATH.

    alt text

    Under older Windows version, you'll need to find the Environment Variables settings. There are several ways to get to them, and it varies by Windows version, but the other answers are valid.

    alt text

    A third is process-specific:

    • The current process path. Affects the current process and any new processes it creates.

    If you're in a PowerShell session, the PATH is visible as $env:PATH. To add something, use +=. I'd recommend using env. vars. instead of hard paths where possible.

    PS > $env:PATH += ";C:\Python26\"
    PS > $env:PATH += ";$($env:ProgramFiles)\Bar\"
    

    Normally you'd do this in your profile. The path can be found at $profile:

    PS > notepad $PROFILE
    

  • Martin

    You can set the system variable PATH. Here you can store several directories which will be searched when you enter a command on the command line and this command is not found in the current directory.

    Go to system control and search for "path" in the search box.

  • Lukas

    Add folders C:\Python26\ and C:\Python26\scripts to path. Go to system administration -> Environment -> Locate path and add your folders. Do not delete anything.