How can I both pipe and display output in Windows' command line?
2014-07
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.
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;
}
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"
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.
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 ] [ ]
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"
}
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.
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.
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.
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
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.
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.