pipe operator in batch script - windows

Sorry if this is a duplicate question :-)
I got below line of code from a batch script. (script.bat)
echo %DATE% %TIME% some text ... >> %logfile% | c:\path\script1.bat >> %logfile%
What is the significance of pipe (|) symbol here?
The script.bat is working fine from command line (script.bat), and it is running script1.bat as well.
While running script.bat from scheduled task it intermittently failed to run script1.bat, exiting with errorlevel 1.
Anyone have any idea what this | doing here :-)

I'm assuming you know what pipes are normally used for. In your code, both sides of the pipe have their stdout redirected to a file. So the pipe is not fulfilling its normal purpose.
The way Windows implements pipes is to launch a new cmd thread for each side of the pipe.
In your case, the | is a quick and dirty way to instantiate parallel processing of the two commands.
Update
I just realized that both sides of the pipe are redirecting stdout to the same log file. That should never work, because only one cmd process (or thread) at a time can have the file open for write access. It certainly does not work for me from the command line. I get the following error: The process cannot access the file because it is being used by another process. The only way that does not fail is if logfile is set to nul.
If I had to guess, I think the original developer intended to use & instead of |. There is no reason to run a single ECHO statement in parallel with another batch script. It looks like the first statement is simply a timestamp.
I think your code should look like
echo %DATE% %TIME% some text ... >> %logfile% & call c:\path\script1.bat >> %logfile%
or better yet
>>%logfile% (
echo %DATE% %TIME% some text ...
call c:\path\script1.bat
)

Related

Stream 3 handle redirection causes output flood

I was playing with wmic when this command
wmic<nul 3>nul
produced this:
The strange thing is, any command will work, but only stream 3 causes the output flood.
I googled and searched on SO "cmd output flood" and cannot find any duplicates nor any results.
Question: Why only stream 3 works?
Update: Likewise,
>con 3<con :
completely disables STDOUT!
I think that, as pointed by aschipfl, my answer to their question explains what the source of the problem is.
Please refer to them for the inner details, this answer is just a summary to explain the different behaviour.
When a redirection is created the code inside cmd handling this task tries to avoid problems saving the original handles to the streams that are being redirected so the redirection process can be reverted.
But the way the used function (standard _dup()) works and the order used when creating the redirection can alter the internal structures used in such a way that at the end the code reversing the redirection uses wrong information.
In short:
<nul 3<nul wmic
<nul saves the stdin handle in &3 and assigns nul stream to &0
3<nul saves the handle inside &3 into &4 and assigns nul stream to &3
wmic is executed. As it is reading from nul (assigned to &0) it can not read anything and ends.
&0 is restored from &3 where it was saved, BUT now &3 points to nul
&3 is restored from &4. Now it points to the original stdin
In the original aschipfl's question the observed behaviour was command prompt closing. After code execution
< file1 3< file2 echo/
the redirection cancel process leaves stdin stream pointing to a disk file (file2). cmd starts to read this file for commands to execute (as it normally does from stdin) and at end of file the read operation fails and the cmd instance ends. If you want a convoluted way of seeing it in work to start notepad you can try
( >file1 echo notepad ) & ( <nul 3<file1 break )
But in this question the behaviour is different because the stream assigned to &3, that will be copied to stdin once the redirection is reverted, is not a disk file but nul
<nul 3<nul echo/
Once the command is executed and redirections are reverted stdin stream is associated with nul device
cmd shows the prompt and tries to retrieve a new command reading from nul
The read operation ends but does not retrieve anything so cmd does not execute anything
goto 2
The question "Why only stream 3 works?" (in the sense of it does not work ;) ) is not correct. You can do something like
1>file1 <nul 4<nul echo/
and get the same result using different streams. Or you can do
3<nul <nul echo/
and it will work (in the sense of it works).
The order in which you request the redirection of the streams, how they are handled by cmd internal code and the existence of still active previous redirections can decide the success or failure of the command.

How to redirect STDIN, STDOUT, STDERR to a file in windows batch script

I have seen a number of examples where you can redirect the STD in/out/err of a single command to a file. So I was able to create a myLogger.bat as a wrapper that redirects the call statement of myScript to myLogFile.
Sample code of myLogger:
CALL "%xDir%\%%1" > "%lDir%\%lFil%" 2>&1
From the command line I execute> myLogger.bat myScriptToBeLogged.bat
However I was not able to redirect STD in/out/err of all the commands in a windows batch file(.bat)
Is there anything similar to "exec > $logFile 2>&1 in shell" that could be included in the beginning of the batch script which will redirect all the logs of the same script to a file.
Thanks in advance!
Mostly, you will want to do this with 1> (STDOUT) and 2> (STDERR)
So, for example:
dir 1>stout.txt 2>stderr.txt
For more details, and how to do clever things with STDIN, see here.
You almost have it. Simply CALL a routine at the beginning with redirection.
#echo off
>test.log 2>&1 call :start %*
exit /b
:start
REM rest of script goes here.

Writing to file with batch

I'm trying to get a batch file to take user input and place it in a file... here is my code so far.
set /p input path=Path:
echo %path% >> log.txt
when I turn echo off, it's putting a "1" infront of the chevrons like so:
echo C:/Example/Path 1>> log.txt
the system can not find the file specified.
Please can anyone explain this
Certainly - I'll answer the question asked.
Originally, >somewhere or any redirection sent the data to the destination specified.
With the changes to the NT version, this was expanded. A digit DIRECTLY preceding a redirector means logical file number where 0=STDIN (standard-input) 1=STDOUT (standard output) and 2=STDERR (Standard error.) The others are undefined. This can cause problems where the data (typically) to be output ends with a digit, hence the >filename echo ... syntax
Consequently, for backward compatibility, (eg) >nul is still processed as it always has been, but it's ECHOed in its explict form 1>nul - sending STDOUT to nul to distinguish it from 2>somewhere_else
try this:
set /p "MyNewPath=Path: "
>>log.txt echo %MyNewPath%
Your SET statement is wrong. :-) Also, PATH is already assigned in the environment (the Windows PATH, and altering it may cause problems with running other commands in your batch file.
Try this instead:
set /p "Input=Path: "
echo %Input% >> log.txt

Problem with `pause` command when redirecting a command-line log to file

Let's say we execute a command as below and redirect the console output into text file.
My issue is that there is pause commands within the batch script and when redirecting like this, I cannot know when to hit enter to continue the batch.
Please help me to get the batch "ignores" the pause commands without changing the batch itself. I prefer to get some redirect/pipe syntax.
MyBatchScriptWithPause.bat > SomeFile.txt
This should do it:
(echo.&echo.&echo.&echo.) | MyBatchScriptWithPause.bat > somefile.txt
assuming that no other command is expecting user input in that batch file.
Edit
It also assumes that only a single pause command is in that file. Otherwise Andriy's suggestion should work.
MyBatchScriptWithPause.bat > SomeFile.txt < nul
nul is a DOS device that will provide infinite null data, so it will act as input whenever the script needs some. It is still available even on modern Windows versions.
I am not sure about ignoring the pauses, but you could redirect them to standard error:
pause 1>&2
which would allow you to know when a pause had occurred.

Is there a way to redirect stderror to stdout for the duration of a script in Windows?

As a long time UNIX shell scripter / release engineer coming into the Windows environment full time with my latest job, there are some things I inevitably miss.
One of them is my trusty old 'exec 2>&1' which in the Bourne shell permently knots together stderr and stdout for the duration of the script, so that logging will capture everything good and bad.
I realize you can append 2>foo.log to the end of every command line invocation, but I'm wondering if there's a better way.
Is there any equivalent in the Windows environment? We're using both .BAT file scripts and Perl scripts here (Mostly for build automation but also some other things) and I'd dearly love to know if this is possible at all.
In Perl you can do
open STDERR, '>&STDOUT' or die "Can't dup STDOUT";
to redirect the STDERR output of the script to STDOUT. See the open documentation for more info. I wouldn't expect this to effect the output of system calls, i.e. system or backticks.
You can nest the .BAT that does the real work inside a .BAT that redirects the streams for logging.
Something like
rem log.bat
cmd /c work.bat 1>>logfile.log 2>&1
rem work.bat
make all
perl cleanups.pl
might do the trick. You might be able to use CALL instead of directly invoking a second CMD.EXE, I haven't checked. If that does work, then the solution can be self contained: just call yourself with an extra argument to indicate the logging is in effect and with the streams redirected.
You can apply redirection to command blocks as well as single commands
(
echo 1
dir
echo 2
) >somefile.txt
Note that it is impossible to use labels and goto inside of command blocks, though.
Read this KB article from MS. Sample taken from the said article:
You can print the errors and standard output to a single file by using the "&1" command to redirect the output for STDERR to STDOUT and then sending the output from STDOUT to a file:
dir file.xxx 1> output.msg 2>&1
The 2>&1 thing seems to be working for me for scripts:
testcmd.cmd > stdboth.txt 2>&1
What exactly are you doing and what are you expecting when you're doing it?
:log_me
#if "%~1" NEQ "*" ("%~nx0" * >> "%~n0.log" 2>&1)
:script
echo this is success
echo this is error>&2
And, if your batch script has command line parameters:
:log_me
#call :script %* >> "%~n0.log" 2>&1
exit /b
:script
echo this is success
echo this is error>&2

Resources