On Windows, I have written a simple bat script that calls another tool. However, this tool outputs a few specific debug lines when using certain options (seems to be a bug in the original code, which I can not/don't want to modify).
Is there a way to suppress lines output by a command that matches a certain pattern (like starting with DEBUG: )?
(FYIO: the tool is latexdiff.exe, and there are some print STDERR "DEBUG:... lines in the perl source code that are not conditional to the debug variable and printed everytime the --flatten option is used. I don't want to suppress stderr completely either.)
You could try the following:
latexdiff.exe 2>&1| findstr /v /b "DEBUG:"
The /v option basically turns the pattern around, i.e. findstr.exe will let everything through, not matching the pattern. The /b option simply says that the pattern should occur at the beginning of a line.
The 2>&1 redirects STDERR to STDOUT and is required because, as you said, the lines are written to STDERR, not STDOUT. Note that as a "side effect" all output now is written to STDOUT.
Update if there is other output on STDOUT that you need to have, you could do something like this
latexdiff.exe old.tex new.tex > diff.tex 2> latexdiff.stderr
type latexdiff.stderr | findstr /v /b "DEBUG:"
That is, redirect STDOUT to your diff file, redirect STDERR to some file. Afterwards, you just type the file to see error messages.
You might want to put that into a batch file of it's own, like so:
#echo off
setlocal
REM determine a suitable temporary filename
set error_file=%TEMP%\latexdiff.%RANDOM%.stderr
REM run actual diff and save its exit code for later
latexdiff.exe "%~1" "%~2" > "%~3" 2> "%error_file%"
set error_level=%ERRORLEVEL%
REM dump error messages
type "%error_file%" | findstr /v /b "DEBUG:"
REM remove temporary error file and exit with latexdiff's exit code.
del /q "%error_file%"
exit /b %error_level%
You can then call it like: latexdiff_wrapper.cmd old.tex new.tex diff.tex. Appart from using temporary files, the downside is, that error messages will not appear while processing, but at the very end. If that is not an issue, it shouldn't because the diff should be fast, you might find that solution useful.
Related
I have the problem that my batch is on a network drive and not all people have write permission to this path. Normally the batch logs also some information.
I would like to suppress the permission denied info in case of no write permission. My assumption is that I have to redirect the standard error to nul.
But this example is not working. I will get a permission denied. It's OK if the log is not created, but I don't want to see a permission denied error message. This error message should be redirected to stderr.
echo test 1> c:\Windows\log.txt 2> nul
If you use the syntax
echo data >log.txt 2>nul
you are requesting to send the output generated by the echo command to the log file and the errors generated by the command to nul.
Your problem is that the output to stderr is not generated by the execution of the echo command, but from a failure in the redirection handling rutines that are executed before the echo itself.
You need to move the stderr redirection operation one level up
2>nul ( >log.txt echo data )
Now, the stderr redirection wraps everything inside the parenthesis, that is, the redirection operation and the echo execution.
The command line
echo test > c:\Windows\log.txt 2> nul
redirects standard output of command ECHO to file c:\Windows\log.txt and the standard error output of command ECHO to device NUL.
So test with the space before redirection operator > is written to c:\Windows\log.txt which usually does not work because this directory is write-protected and used here as an example for the network folder with individual access permissions.
The command ECHO does never output something to handle STDERR. Therefore the redirection of standard error output of command ECHO to device NUL has never any effect.
What you really want is suppressing the error output of cmd on redirection of ECHO output to the file log.txt in perhaps protected folder which requires a different syntax.
You could use:
(>"%SystemRoot%\log.txt" echo test) 2>nul
Or easier to read:
#echo off
(
>"%SystemRoot%\log.txt" echo test
) 2>nul
The ECHO command line is enclosed in a command block starting with opening parenthesis ( and ending with matching closing parenthesis ). Everything output by any command in this command block to handle STDERR is redirected to device NUL which includes the error message output by cmd if the current user has no permission to create file log.txt in output directory, or if the output file exists already and has read-only attribute set, or if the output file is opened currently by an application which opened it with a file sharing write lock for other applications.
The redirection operator > and the file name is specified on ECHO command line at beginning to be able to output also a text with number 1 ... 9 at end without writing a trailing space into the file %SystemRoot%\log.txt.
As a command block is used around ECHO command line, the output of a closing round bracket ) by ECHO requires now escaping this character with caret character ^ as demonstrated in example below:
#echo off
(
>"%SystemRoot%\log.txt" echo '(' and '^)' are round brackets/parentheses.
) 2>nul
A slower alternate solution is using delayed expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "TextToOutput='(' and ')' are round brackets/parentheses. '!' is an exclamation mark."
setlocal EnableDelayedExpansion
(
echo !TextToOutput!>%SystemRoot%\log.txt
) 2>nul
endlocal
endlocal
%SystemRoot% is replaced already by C:\Windows (or whatever is the path to Windows directory) on parsing the entire command block by Windows command interpreter before executing the first line within ( ... ). But the value of environment variable TextToOutput is not replacing !TextToOutput! before ECHO is executed making it safe now to specify the redirection operator at end without a space before to avoid a trailing space in file on output line.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
cmd /?
echo /?
endlocal /?
set /?
setlocal /?
And read also the Microsoft TechNet article Using command redirection operators.
It is also possible to use a completely different technique.
The batch file first redirects everything to a temporary file in folder for temporary files of current user. When finished the temporary log file is copied to network folder or appended to network folder with suppressing the error message if the user has no write permission on network folder.
Example code for usage of this technique:
#echo off
set "LogFile=%TEMP%\%~n0.tmp"
del "%LogFile%" 2>nul
>>"%LogFile%" echo test
rem Other commands with output also redirected to log file.
if exist "%LogFile%" (
rem copy "%LogFile%" "%~dp0log.txt"
copy /B "%~dp0log.txt"+"%LogFile%" "%~dp0log.txt"
del "%LogFile%"
) >nul 2>&1
set "LogFile="
The first COPY command line commented out in batch code above overwrites the log file in directory of the batch file with the newly created log file.
The second COPY command line appends the newly created log file to already existing log file in directory of the batch file.
A digit directly before a redirector redirects that-number device.
1 is standard output, so you are trying to redirect the echo output to c:\windows - probably protected and also a directory, not a file.
If your purpose (you don't say) is to redirect test 1 then
1>file echo test 1 2>nul
but since you don't say explicitly what you want to do, that's a guess.
I'm LEARNING!
The only problem is, I can't seem to get the pipeline redirection operator working. May seem a dumb question.
As all of you know, the pipeline operator retrieves output of first command as input of other command. How can I do what the pipeline operator is supposed to do?
Full code (Don't worry, It's very tiny):
#echo off
title malhunt e-alpha
echo starting...
if exist dlls.txt del dlls.txt
echo done verifying.
echo grabbing dlls...
for /f %%a in ('tasklist /m') do echo %%a >>dlls.txt
type dlls.txt
echo dll grabbing done
pause
for /f %%b in (dlls.txt) do findstr wow.dll, dlls.txt
pause
Simple enough, the line is
findstr wow.dll, dlls.txt|echo *
so now, I can tell that the problem occurs on echo * How exactly do you make echo create output of findstr input?
NOTES
The comma on the end of wow.dll is intentional, because tasklist /m likes to leave commas before the dynamic link libraries. I know that probably there's no wow.dll on the tasklist, but for future reference, how can I create pipeline redirection?
Including the comma to search the dll will fail if the dll is the last one in the list of modules loaded into a process.
You can not pipe into echo command. Echo does not consume data from a pipe or a redirection.
You don't need to pipe into echo command, findstr /c:" wow.dll" dlls.txt will show the result on console
If we have a batch file being redirected to a log like so:
C:\Testing\Example.bat > C:\Testing\Example.log
Is there any way inside the batch file to determine if there's a standard output redirect happening to a log file?
Basically the batch file I have requires three arguments passed to it. When arguments are left out, the batch file prints a usage example much like regular Windows commands would, and throws a 'pause' out so instructions can be read. However if the batch is called without arguments AND it's being logged, then the batch file will just sit there forever waiting for a key to break the pause, but won't show anything on the screen.
Normally this wouldn't be a problem at all, however I'm trying to make this idiot-proof since I won't be the one implementing the batch file in other scripts/scheduled tasks.
At this point it seems like I need to get rid of the usage pause entirely, but I was hoping for a solution where I wouldn't have to. Thanks for the help.
There is no good way to determine if stdin or stdout has been redirected using native batch commands.
But there is no need to worry about redirection in your case. Simply redirect the PAUSE stdin and stdout to con, and you don't have to worry about prior redirection.
pause <con >con
Update
It is possible to cleanly detect whether stdin and/or stdout and/or stderr has likely been redirected. But I haven't figured out a way to non-destructively determine which of those handles was redirected.
#echo off
2>nul (5>&4 break) && (
>con echo There has been redirection
) || (
>con echo No redirection found
)
The technique relies on the fact that whenever an existing handle is redirected, the old definition is saved in the lowest available undefined handle.
If there has not been any redirection, then 2>nul saves stderr in 3, and 4 is undefined. So 5>&4 fails, and the || branch is fired.
If there has been redirection, then 3 has already been defined to preserve the original value of the redirected handle, so 2>nul will save stderr in 4 (unless 4 has also already been used by some other redirection). Either way, the 5>&4 will succeed because 4 is defined, so the && branch will fire.
If the test reports that there has been redirection, then at least one of the following must be true:
stdin redirection
stdout redirection
stderr redirection
handle 4 defined directly with something like 4>"log.txt"
handle 5 defined directly with something like 5>"log.txt"
Unfortunately, I cannot determine which of the above are true, except for...
Over at DosTips, SiberiaMan has published a simple technique to determine if stdin has been redirected or is receiving a pipe:
2>nul >nul timeout /t 0 && (
>con echo stdin not redirected or piped
) || (
>con echo stdin has been redirected or is receiving piped input
)
I am not aware of any way to do this using only batch and standard commands. However, there are tricks that can be used from other languages such as native C or C# programs, if it's important enough to you to include another .exe along with your .bat.
Here is a sneaky approach. This batch file creates a .com file, then calls it to test the stdout handle: http://www.dostips.com/forum/viewtopic.php?f=3&t=2800
This explains how to do it with C#, if you want to write a simple C# helper programme: Check if output is redirected. That makes me think it should also be possible from Powershell, but I haven't tried.
There isn't a way in standard Windows batch files (that I'm aware of) to determine where STDOUT is being redirected, or even if it is being redirected. However, you might wish to write your usage examples out to STDERR instead of STDOUT, so at least a simple redirect into a file will not capture the usage info.
Example test.cmd:
#ECHO OFF
ECHO Test output 1>&2
PAUSE 1>&2
Then call it:
test.cmd > output.log
Which will still output:
Test output
Press any key to continue . . .
Of course, this does nothing for when the file is called with STDERR being redirected.
test.cmd 2> error.log
I think you just ask the wrong question. you want to find a way to end the batch with extra break when in cmd mode. and stop in batch mode without pausing.
You can use CMDpause
this will make a variable named cmdpause
examples
https://www.administrator.de/wissen/batchcode-erstellen-fehler-batch-leichter-finden-184736.html
https://www.dostips.com/forum/viewtopic.php?t=7257#p47510
there is something from Dave and jeb. But I have no link.
Phil
You could check if stdout is redirected with a small trick.
It uses the fact that outputting backspaces after a TAB moves the cursor back and if the cursor shall be moved before the home position in this situation, an error will be created to get a hint in your logfiles for the strange characters.
The drawback is, that it outputs <FF><TAB>><BACKSPACE><BACKSPACE><CR><LF> to stdout.
When stdout is the console, it simply clears it.
But when stdout is redirected to a file you append these characters to the file, but it can be commented with a meaningful text.
#echo off
setlocal
call :createTAB_BS
echo Script started, test for redirection:
cls
( echo %%TAB%%%%BS%%%%BS%%) | ( findstr "^" 2^> nul)
if %errorlevel% EQU 0 (
>CON echo stdout is redirected
) ELSE (
echo stdout goes to the console
)
exit /b
:createTAB_BS
for /f "tokens=1,2 delims=," %%A in ('forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(0x09,0x08"') do (
set "TAB=%%A"
set "BS=%%B"
)
exit /b
There has been variants of this question asked for generations, but despite writing some quite complicated Windows scripts, I can't seem to find out how to make them actually silent.
The following is an excerpt from one of my current scripts:
#ECHO OFF
SET scriptDirectory=%~dp0
COPY %scriptDirectory%test.bat %scriptDirectory%test2.bat
FOR /F %%f IN ('dir /B "%scriptDirectory%*.noext"') DO (
del "%scriptDirectory%%%f"
)
ECHO
The result of this is:
C:\Temp> test.bat
1 file(s) copied.
File Not Found
Echo is off.
C:\Temp>
Whereas the "1 file(s) copied." is just annoying, the "File Not Found" makes the user think that something has gone wrong (which it hasn't - no files is fine).
To suppress output, use redirection to NUL.
There are two kinds of output that console commands use:
standard output, or stdout,
standard error, or stderr.
Of the two, stdout is used more often, both by internal commands, like copy, and by console utilities, or external commands, like find and others, as well as by third-party console programs.
>NUL suppresses the standard output and works fine e.g. for suppressing the 1 file(s) copied. message of the copy command. An alternative syntax is 1>NUL. So,
COPY file1 file2 >NUL
or
COPY file1 file2 1>NUL
or
>NUL COPY file1 file2
or
1>NUL COPY file1 file2
suppresses all of COPY's standard output.
To suppress error messages, which are typically printed to stderr, use 2>NUL instead. So, to suppress a File Not Found message that DEL prints when, well, the specified file is not found, just add 2>NUL either at the beginning or at the end of the command line:
DEL file 2>NUL
or
2>NUL DEL file
Although sometimes it may be a better idea to actually verify whether the file exists before trying to delete it, like you are doing in your own solution. Note, however, that you don't need to delete the files one by one, using a loop. You can use a single command to delete the lot:
IF EXIST "%scriptDirectory%*.noext" DEL "%scriptDirectory%*.noext"
If you want that all normal output of your Batch script be silent (like in your example), the easiest way to do that is to run the Batch file with a redirection:
C:\Temp> test.bat >nul
This method does not require to modify a single line in the script and it still show error messages in the screen. To supress all the output, including error messages:
C:\Temp> test.bat >nul 2>&1
If your script have lines that produce output you want to appear in screen, perhaps will be simpler to add redirection to those lineas instead of all the lines you want to keep silent:
#ECHO OFF
SET scriptDirectory=%~dp0
COPY %scriptDirectory%test.bat %scriptDirectory%test2.bat
FOR /F %%f IN ('dir /B "%scriptDirectory%*.noext"') DO (
del "%scriptDirectory%%%f"
)
ECHO
REM Next line DO appear in the screen
ECHO Script completed >con
Antonio
You can redirect stdout to nul to hide it.
COPY %scriptDirectory%test.bat %scriptDirectory%test2.bat >nul
Just add >nul to the commands you want to hide the output from.
Here you can see all the different ways of redirecting the std streams.
Just add a >NUL at the end of the lines producing the messages.
For example,
COPY %scriptDirectory%test.bat %scriptDirectory%test2.bat >NUL
Copies a directory named html & all its contents to a destination directory in silent mode.
If the destination directory is not present it will still create it.
#echo off
TITLE Copy Folder with Contents
set SOURCE=C:\labs
set DESTINATION=C:\Users\MyUser\Desktop\html
xcopy %SOURCE%\html\* %DESTINATION%\* /s /e /i /Y >NUL
/S Copies directories and subdirectories except empty ones.
/E Copies directories and subdirectories, including empty
ones. Same as /S /E. May be used to modify /T.
/I If destination does not exist and copying more than one
file, assumes that destination must be a directory.
/Y Suppresses prompting to confirm you want to overwrite
an
existing destination file.
I have a batch file that executes a java application. I'm trying to modify it so that whenever an exception occurs, it'll write the STDERR out to a file.
It looks something like this:
start java something.jar method %1 %2 2>> log.txt
Is there a way I can write the arguments %1 and %2 to the log.txt file as well? I don't want to write it to the log file everytime this batch file gets called, only when an exception occurs.
I tried searching for a way to redirect STDERR into a variable, but I couldn't figure it out. Ideally I'd like the log file to look something like:
Batch file called with parameters:
- "first arg"
- "second arg"
Exception:
java.io.exception etc...
------------------------------------
Batch file called with parameters:
- "first arg"
- "second arg"
Exception:
java.io.exception etc...
Something like this might work:
javastart.cmd
#echo off
set params=%*
for /f "delims=" %%e in ('java something.jar method %1 %2 ^>nul') do (
echo Batch file called with parameters:>>log.txt
echo - Args[]: %*>>log.txt
echo - Arg[1]: %1>>log.txt
echo - Arg[2]: %2>>log.txt
echo Exception: %%e
)
I am not a java programmer myself, I cannot test this output/situation but:
1: %* means every parameters, from 1st to last (even %12 although it's not directly available.. ) whatever the formating. Might be better to use this than delimit those. In the even of bad spacing/quoting, you would have the full parameters too. I added a line in the log, to show the full line then the parameters. You can see where the problem was if it's a matter of bad arguments.
2: sending the stdout to null (^>nul) will only keep the stderr output, set into %%e
3: Whatever is in the do part will only happen is the for test statement actually has anything as an output, i.e. if %%e is set.
With those being said, you have to test the script and see if the exception is actually sent to stderr or, like some other softwares, to stdout even if an error occured.
I hope this helps.
The only working solution I see would be to redirect stderr to a temporary file
java blah.jar %1 %2 2>stderr
and afterwards looking whether something has been written to the file and writing to the log in that case.
for %%i in (stderr) do if %%~zi GTR 0 (
echo Parameters: %1 %2 >>log.txt
type stderr >> log.txt
)
If the batches aren't run in sequence but rather simultaneously you need to find something to uniquify the temp variable:
set file=%time::=%
set /a file=file
set file=%file%%random%
java blah.jar %1 %2 2>stderr_%file%
for %%i in (stderr) do if %%~zi GTR 0 (
echo Parameters: %1 %2 >>log.txt
type stderr >> log.txt
)
This will avoid clashes between multiple running batches. However, you are currently not using anything to lock writing to your log file and things may appear out of place when other things get written to it (with other batches you might get interleaving in the echo and type commands or, when you're redirecting output of java to that file as well, then it may get mixed up with regular output of the java program:
Parameters: foo bar
Some regular output
Parameters: foo2 bar2
More output
NullPointerException at Blah: What we really wanted to have right after the parameters
IOException at Blah: This exception belongs to the parameters foo2 bar2
You can use another file as semaphore for writing to the log to avoid the batch outputs getting mixed: create the file [copy nul file] when you want to write to the log, delete it afterwards and before you attempt to create it check whether it is actually there and wait until it disappears. You can't do anything about the stdout log being mixed into the file, though, except you use that temp file approach for stdout as well (and simply type the stdout log to log.txt when the Java program finished, but, again with using the semaphore.
I don't get exactly what you are asking, but here's some info that may help...
You can redirect stderr separately from stdout in a .cmd or .bat file. To redirect stderr, use something like this:
MyCommand.exe > command.stdout.txt 2> command.stderr.txt
Then, you can check the command.stderr.txt for content, and if any is present, concatenate it to the command.stdout.txt into your log file. Or you could concat it in any case. If you like you could also echo the command that you ran, into the final log file.
You can also check for the exit code in a batch file, using the %ERRORLEVEL% env var. Exe files are expected to set ERRORLEVEL in case of an error. I don't know if java.exe does this. It should, if it is a good citizen on Windows. This might be an alternative way of finding out if the Java app exited with an error condition. But this is no guarantee that stderr got nothing. For example, a Java app might print out an exception and stack trace, and then exit with code 0, which indicates success. In which case stderr would have gunk in it, but ERRORLEVEL would be zero.
EDIT: s/ERROR_LEVEL/ERRORLEVEL
How about something like this (untested):
#echo off
#echo Batch file called with parameters: >> log.txt
#echo - %1 >> log.txt
#echo - %2 >> log.txt
start java something.jar method %1 %2 2>> log.txt
Try using ERRORLEVEL
java something.jar method %1 %2 2>> log.txt
IF %ERRORLEVEL% NEQ 0 GOTO Error
Error:
#ECHO There was an error
#ECHO Arg 1: %1 >> log.txt
#ECHO Arg 2: %2 >> log.txt
A batch file like this should do the trick.
start_prog.cmd
CALL :Start_Prog arg1 arg2
GOTO :EOF
:Start_Prog
something.py %1 %2 2>&1 1>nul | "C:\Python26\python.exe" format_output.py %1 %2 >>log.txt
GOTO :EOF
Here I'm passing stderr via a pipe and passing arguments as arguments. The output is then appended to a log.txt file.
Throw away stdout
1>nul
Redirect stderr to stdout
2>&1
Pipe the stderr into a script to format the output, and pass arguments as arguments.
| "C:\Python26\python.exe" format_output.py %1 %2
Append output to a "log.txt".
>>log.txt
On my system I need to call python.exe otherwise the pipe doesn't work.
Here are the two files I used "something.py" and "format_output.py".
something.py
import sys
print >>sys.stdout, " ".join(sys.argv[1:])
print >>sys.stderr, "java.io.exception etc..."
format_output.py
import sys
print "Batch file called with parameters:"
for arg in sys.argv[1:]:
print '- "{0}"'.format(arg)
print "Exception:"
for line in sys.stdin:
print line
And finaly here's the output.
log.txt
Batch file called with parameters:
- "arg1"
- "arg2"
Exception:
java.io.exception etc...
The only thing missing is that something is always written to the "log.txt" file.
To tidy this up further I would move writing the log file into "format_output.py".
You could then add a check to see if the stderr from your program is blank.