How do I make a Windows batch script completely silent? - windows

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.

Related

How to move files from specific subdirectories to a subdirectory in base directory of a directory tree?

The base directory has about 20 subdirectories. Each subdirectory has many files. I need to move all the files from specific subdirectories to a newly created subdirectory in base directory at once.
For example I have in base directory D:\Documents the following directories:
D:\Documents\12345\data\images\
D:\Documents\12345\test\
D:\Documents\12345\documents\
I need to move all the files under images into newly to create directory D:\Documents\images in base directory.
Can you please help me in this?
This small batch file makes the job:
#echo off
md D:\Documents\images 2>nul
for /F "delims=" %%I in ('dir D:\Documents\* /A-D /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /B /I /R /C:D:\\Documents\\..*\\images\\ 2^>nul') do (
move /Y "%%I" "D:\Documents\images\%%~nxI"
rd "%%~dpI" 2>nul
)
The batch file first creates the target directory with suppressing the error message output by MD if this directory already exists. It is expected by this batch file that it is possible to create this directory and move the files into this directory without any additional checks.
Command DIR searches in D:\Documents and all it subdirectories because of /S for just files because of /A-D (attribute not directory) and outputs them in bare format because of /B which means just the file name with file extension and in this case also with full path because of /S.
It is possible that DIR cannot find any file in entire D:\Documents directory tree. The error message output in this case to handle STDERR is suppressed by redirecting it to device NUL using 2>nul after command DIR.
This output by DIR is redirected as input for command FINDSTR used as filter. It runs a regular expression find searching for lines starting with D:\Documents\ having at least one more character before \images\ must be found too. So it ignores the files in directory D:\Documents\images\ in case of this directory already exists with files on starting the batch file. But it does not filter out files in for example D:\Documents\12345\data\images\Subfolder\ as this regular expression does not check if \images\ is found at end of path.
It is possible that FINDSTR does not find any line (file name) matching the regular expression. The error message output in this case is suppressed by using 2>nul after command FINDSTR.
The command line with DIR and FINDSTR is executed by FOR in a separate command process started with cmd.exe /C in background without a visible window. For that reason the redirection operators > and | must be escaped with ^ to be interpreted first as literal characters by the Windows command interpreter on parsing the entire FOR command line before execution of FOR.
The lines output by the command line with DIR and FINDSTR to handle STDOUT in separate command process is captured by FOR and then processed line by line. With delims= the default behavior of splitting each line up into tokens using space and horizontal tab as delimiters is disabled by specifying an empty delimiters list.
The command MOVE moves the found file to D:\Documents\images\ with overwriting a file with same name in that directory.
The command RD removes the directory of just moved file if this directory is empty now after moving the file. Otherwise on directory not yet being empty an error message is output by command RD to handle STDERR which is suppressed using once again 2>nul.
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.
dir /?
echo /?
findstr /?
for /?
md /?
move /?
rd /?
Read also the Microsoft article about Using Command Redirection Operators.

Batch file for loop executes on one machine only

I have written the following .bat file, and it runs perfectly on my Windows 2000 machine, but will not run on my Windows 7 or Windows XP machines. Basically it just loops through the current directory and runs a checksum program which returns the checksum. The output of the program is saved to a text file and then formatted to remove the checksum of the output file.
#Echo Off
for /r %%f in (*.txt) do crc32sum.exe %%f >> all_checksums.txt
ren all_checksums.txt old.txt
findstr /v /e /c:"all_checksums.txt" old.txt > all_checksums.txt
del old.txt
When I run this file on my Win2k PC with a bunch of text files and the crc32sum.exe in a folder, it outputs the file. On other machines it outputs a blank file. I turned Echo on and kept only the for loop line and found that the output from executing the crc32sum.exe is nothing. If you manually run the crc32sum.exe file it outputs the checksum no problem.
Any ideas as to how to fix this?
EDIT: Here is a link to the software: http://www.di-mgt.com.au/src/digsum-1.0.1.zip
EDIT2: New development, it seems that the file works if the path of the folder has no spaces in it i.e. C:\temp or C:\inetpub\ftproot or C:\users\admin\Desktop\temp. Does anyone know how I can make this work with paths that have spaces? %%~f doesnt work it says unexpected.
Try this modified batch code which worked on Windows XP SP3 x86:
#echo off
goto CheckOutput
rem Command DEL does not terminate with an exit code greater 0
rem if the deletion of a file failed. Therefore the output to
rem stderr must be evaluated to find out if deletion was
rem successful or (for a single file) the file existence is
rem checked once again. For details read on Stack Overflow
rem the answer http://stackoverflow.com/a/33403497/3074564
rem The deletion of the file was successful if file created
rem from output message has size 0 and therefore the temp
rem file can be deleted and calculation of the CRC32 sums
rem can be started.
:DeleteOutput
del /F "all_checksums.txt" >nul 2>"%TEMP%\DelErrorMessage.tmp"
for %%E in ("%TEMP%\DelErrorMessage.tmp") do set "FileSize=%%~zE"
if "%FileSize%" == "0" (
set "FileSize="
del "%TEMP%\DelErrorMessage.tmp"
goto CalcCRC32
)
set "FileSize="
echo %~nx0: Failed to delete file %CD%\all_checksums.txt
echo.
type "%TEMP%\DelErrorMessage.tmp"
del "%TEMP%\DelErrorMessage.tmp"
echo.
echo Is this file opened in an application?
echo.
set "Retry=N"
set /P "Retry=Retry (N/Y)? "
if /I "%Retry%" == "Y" (
set "Retry="
cls
goto CheckOutput
)
set "Retry="
goto :EOF
:CheckOutput
if exist "all_checksums.txt" goto DeleteOutput
:CalcCRC32
for /R %%F in (*.txt) do (
if /I not "%%F" == "%CD%\all_checksums.txt" (
crc32sum.exe "%%F" >>"all_checksums.txt"
)
)
The output file in current directory is deleted if already existing from a previous run. Extra code is added to verify if deletion was successful and informing the user about a failed deletion with giving the user the possibility to retry after closing the file in an application if that is the reason why deletion failed.
The FOR command searches because of option /R recursive in current directory and all its subdirectories for files with extension txt. The name of each found file with full path always without double quotes is hold in loop variable F for any text file found in current directory or any subdirectory.
The CRC32 sum is calculated by 32-bit console application crc32sum in current directory for all text files found with the exception of the output file all_checksums.txt in current directory. The output of this small application is redirected into file all_checksums.txt with appending the single output line to this file.
It is necessary to enclose the file name with path in double quotes because even with no *.txt file containing a space character or one of the special characters &()[]{}^=;!'+,`~ in its name, the path of the file could contain a space or one of those characters.
For the files
C:\Temp\test 1.txt
C:\Temp\test 2.txt
C:\Temp\test_3.txt
C:\Temp\TEST\123-9.txt
C:\Temp\TEST\abc.txt
C:\Temp\TEST\hello.txt
C:\Temp\TEST\hellon.txt
C:\Temp\Test x\test4.txt
C:\Temp\Test x\test5.txt
the file C:\Temp\all_checksums.txt contains after batch execution:
f44271ac *test 1.txt
624cbdf9 *test 2.txt
7ce469eb *test_3.txt
cbf43926 *123-9.txt
352441c2 *abc.txt
0d4a1185 *hello.txt
38e6c41a *hellon.txt
1b4289fa *test4.txt
f44271ac *test5.txt
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.
cls /?
del /?
echo /?
for /?
goto /?
if /?
rem /?
set /?
type /?
One of the help pages output on running for /? informs about %~I, %~fI, %~dI, %~pI, %~nI, %~xI, %~sI, %~aI, %~tI, %~zI.
Using in a batch file f (in lower case) as loop variable and referencing it with %%~f is a syntax error as command processor expects next the loop variable. %%~ff would be right, but could be different to %%~fI (name of a file/folder with full path and extension without quotes) in comparison to %%~I (string without surrounding quotes).
It is not advisable to use (those) small letters as loop variable. It is better to use upper case letters or character # as loop variable. The loop variable and also those modifiers are case sensitive while nearly everything else in a batch file is case insensitive.

Change extension of selected files with CMD

I have a folder with well over 400 RAR, ZIP and 7Z files. I want to make a bat file that change the extensions of selected files in this folder as follows, RAR->CBR, ZIP->CBZ and 7Z->CB7 without renaming files not selected.
I have tried with:
ren %1 *.cbr
and:
ren %~n1.rar *cbr
but it does not work.
The bat file is going to be placed in the Send To menu.
I want, if possible, to use only cmd, as I don't know any scripting, or programming language.
Thanks
[This answered your original question, which as more about "all" or multiple files.]
You can use the FOR loop. Type for /? for details.
First, try the FOR command out to make it ECHO (print) the filename. You can use this to test what you want/think it's going to do:
for %f in (*.rar) do echo %f
Then, to actually rename, you'll need something like:
for %f in (*.rar) do ren %f *.cbr
[Following your edit]:
If you're calling a batch file from 'Send To' or whatever, your select file should come in in parameter %1 (and %2, %3 etc if multiple). You may also be able to use %* for all parameters.
Try echoing it somewhere, to console or a file, to test whether you're receiving these & what's happening. Save the following as a batch file, and try it:
echo %1
pause
In a batch file, % symbols need to be doubled up (for parsing reasons). So to rename, you might try something like:
for %%f in (%*) do echo %%f
for %%f in (%*) do ren %%f *.cbr
See also: http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/percent.mspx?mfr=true

Bulk renaming files in relation to the folder names

I am very new to coding and bulk processes but i am looking for a command line SPECIFICALLY for windows command prompt and i am wondering if such a thing exists. So I have a folder containing 111 subfolders, with each subfolder containing between 20 and 40 png image files. Each subfolder is named 001-111 accordingly and the png files are ordered how i want them, however i am looking for a command line that would be able to quickly and efficiently name all the pngs in the folders to the name of the folder followed by the png number in brackets
e.g. for folder 037, i would want the png's to be renamed to: 037(1), 037(2), 037(3) etc...
I am hoping for the best although i am unsure such a code may not be possible or be simply done.
Also if you come up with a code that achieves this process, it would be great if you could reply with the simple command line that i could use rather than a full explanation because i am new to coding and far from fluent with the language or terms or how things work. I know this same process can be achieved by going select all>rename (ctrl a>f2) and renaming to the folder name however i need to use this process frequently and dont want to have to open each folder, i would rather have a command line for cmd that would do it swiftly
Thank you and a simple answer would be greatly appreciated
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "parentdir=u:\parent"
FOR /l %%a IN (1001,1,1111) DO (
SET dir=%%a&SET "dir=!dir:~1!"
FOR /f "delims=" %%i IN ('dir /a-d /b "%parentdir%\!dir!\*.png" 2^>nul') DO (
ECHO REN "%parentdir%\!dir!\%%~nxi" "!dir!(%%~ni)%%~xi"
)
)
GOTO :EOF
Test results:
Starting directory :
u:\parent\001\1.png
u:\parent\037\1.png
u:\parent\037\2.png
u:\parent\111\999 with spaces in name.png
Script response
REN "u:\parent\001\1.png" "001(1).png"
REN "u:\parent\037\1.png" "037(1).png"
REN "u:\parent\037\2.png" "037(2).png"
REN "u:\parent\111\999 with spaces in name.png" "111(999 with spaces in name).png"
Obviously, you'd need to replace the value assigned to parentdir with your actual target directory name.
The script will report the renames it proposes to do. To actually invoke the rename remove the ECHO keyword.
I would create a batch file like so:
renamepng.bat:
cd %%1
if ERRORLEVEL 1 goto end
for %f in *.png do mv "%f" "%%1(%f).png"
cd ..
:end
This will attempt to cd to the directory name provided on the command line, abort if that fails, then rename all the .png files and return to the previous directory
then call it like so:
for %d in ??? do call renamepng.bat %d
which will loop through all 3-character file and directory names in the current directory, can call the batch file on each one. Using call instead of just the batch file name causes execution to return to the loop when the batch finishes.

How to suppress specific lines in Windows cmd output?

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.

Resources