Preventing output from commands in Batch - batch-file

How do you prevent output in the CMD window? I know how to do it for these
#echo off
timeout /t 3 /nobreak >nul
MKDIR Files 2>nul
But I was wondering what the general way is for all commands, or how to find out for each command that way I don't have to keep asking. I'm trying to get XCopy to copy silently, that way the screen doesn't get spammed, but I have a feeling I'll need it for other commands too.

There are 2 output streams: stdout (standard output console) and stderr (standard error console).
With >nul or more correct 1>nul the text written to stdout is redirected to the null device.
With 2>nul the text written to stderr is redirected to the null device.
>nul and 2>nul can be used on any command and also both at the same time and should be always at end of the line. They can be also used at beginning of a line which makes sense in some special cases, but most often the redirecting operators are written at end of a line.
Most console commands and applications print a help when running with option /? (Windows) or -? (when ported from Unix). Open a command prompt window and try that with xcopy /?
Some commands like xcopy have special options to suppress output to stdout. The option for xcopy is /Q which is short for quiet. Using this option makes xcopy also faster a little bit as printing the output to stdout needs some time and is of course wasted time if the output is redirected to null device.
But take care of possible halt on errors when redirecting all output to null device. For example on using xcopy the options /C and /R should be used additionally to avoid a halt of the copy process on error or a read-only file in target directory with no information of the user who started the copy process why batch job does not finish because of using /Q and additional >nul 2>nul.
In batch files always >nul should be used and never 1>nul although that is often also possible. The command line interpreter replaces every >nul on execution of the batch file automatically by  1>nul (space + 1>nul). This should be taken into account when echo something into a file to avoid an often unwanted trailing space on each line redirected into a file.
See Microsoft article Using command redirection operators for even more information.

The "general way" is exactly what you're doing, which is to redirect standard output to nul using >nul. It should always be the very last part of each line.
(The mkdir example you're using redirects standard error (2>nul instead of standard output; I'm not sure why it would be doing that, but presumably you know why that's being done.)
The problem with doing so, though, is that you don't get any information about what worked and what failed, which makes it difficult to track down problems. When you need to not show the information on screen but want to make it available later if needed, redirect to a file instead.
REM Redirect first command to log, truncating anything
REM that existed in the file before.
mkdir Files > temp.log
REM Copy some files into the folder just created, appending
REM the results to the same log file
xcopy *.txt Files\ >> temp.log

Related

Dir /b /s different in cmd.exe and batch file

I want to know why there is a delay between dir /b/s C:\*.* in cmd.exe and batch file.
I tried the blow batch file, But it takes about one hour to show me the result, but dis /b/s in cmd.exe show the result fast.
for /f "tokens=*" %%a in ('dir/b/s c:\*.*') do (
echo "%%a"
copy "%%a" C:\windows\ )
Please help me to show the result in batch file fast like cmd.exe.
There are two elements that lead to this behaviour
for /f will always retrieve all the data that it needs to process before starting to process it. Than means that for /f will "sit" (not execute the code in the do clause) while dir works, waiting for all the data.
When for /f reads a disk file it will "simply" acomodate a buffer large enough to load it into memory, load the file and start processing it. But when the data source is a command execution, not knowing the final size of the data, a buffer is defined and resized as needed while retrieveing the command's output.
The need to retrieve all the data and the process of resizing the buffer is what generates the delay.
Why? As a example:
If I use dir /s /b c:\windows I get a list of 119343 files, 13MB of data.
As the memory buffer defined by for /f starts at 4KB and is increased in 4KB each time it is full it will need 3327 resize operations.
Each time a resize is needed, a new 4KB larger buffer is allocated and the data inside the old buffer is copied into the new larger buffer. For 13MB we need 3327 resize operations which means aprox. 21GB in memory copy operations (data to copy increases each time the buffer is resized). Maybe it does not seem a lot and memory is fast, but sometimes (ex. here) things are not so simple.
If you add the time needed to retrieve the data from disk to the time needed to handle the memory allocation/memory copy, before starting to process the data, you have a visible delay.
If you need remove the delay, don't use a for /f. A better option (while keeping a similar approach) could be
for /r "c:\" %%a in (*) do (
echo "%%~fa"
)
That is, use the recursive version of the for command, from the indicated starting folder.
First, dir/b/s c:\*.* is of invalid syntax. It works because of Windows command processor automatically corrects the command line. Correct would be:
dir /b /s C:\*.*
Or 100% valid shorter:
dir /b /s C:\*
Or 100% valid shortest:
dir /b /s C:\
The general syntax on Windows command line is:
command/executableSPACEargument1SPACE"argument 2"SPACE/option
No space between command dir and its first argument /b is not 100% correct syntax. It is in general not good in no scripting and programming language on writing code depending on automatic correction of the syntax by the application interpreting the code. I see this daily on visiting lots of websites with an old browser where the old browser fails to display a webpage right just because of incorrect code in one of the files of the webpage which are displayed fine by latest browsers because of their excessive auto-detection and auto-correction of syntax errors caused by the people writing the files of the webpage.
The execution of the DIR command in the batch file is slower because of FOR with option /F and a set in '...' starts in background using %CompSpec% /C a new command process for execution of the DIR command line. Everything finally output to handle STDOUT is captured by FOR and processed after started cmd.exe terminated itself.
FOR with option /F ignores all empty lines on processing captured output of additional command process. This behavior cannot be changed with options.
FOR with option /F splits up each line into substrings (tokens) using normal space and horizontal tab character as string delimiters. The string splitting behavior can be controlled by using option delims= whereby using this option with specifying no characters turns off the string splitting behavior.
FOR with option /F assigns to specified loop variable by default only the first space/tab separated substring. This behavior can be controlled with option tokens=. The usage of "tokens=*" results in removing all leading spaces/tabs and assign rest of captured line to the specified loop variable.
FOR with option /F ignores also all lines starting with a semicolon by default. This behavior can be controlled by option eol= (end of line).
So what happens on execution of this command line:
for /f "tokens=*" %%a in ('dir/b/s c:\*.*') do ( echo "%%a")
FOR starts in background a command process which executes the command DIR.
DIR searches for non-hidden files and directories on drive C: and all its non-hidden subdirectories and outputs their names with full path to handle STDOUT of the background command process captured by FOR.
In background started cmd.exe terminates itself after DIR finished.
FOR processes the captured lines.
There are no empty lines because dir /B outputs no empty lines.
There are no lines starting with spaces/tabs because dir /B /S results in output of file and directory names with full path starting with drive C: in this case. A file or directory name without full path could begin with one or more spaces.
There are no lines starting with ; also because of dir /B /S. A file or directory name can have a semicolon as first character, but not on being output with full path.
FOR runs the command ECHO for each string assigned to loop variable a.
Better would be the command line:
for /F "eol=| delims=" %%I in ('dir /B /S C:\ 2^>nul') do echo "%%I"
This command line would work also on DIR option /S not used resulting in output of just file/folder name without path even for files/folders starting with a semicolon or a space character. The end of line option is defined with vertical bar because of no file/folder name can contain | according to the Microsoft documentation Naming Files, Paths, and Namespaces.
It is advisable to run a command in a command prompt window with /? as first and only argument to get displayed the help/documentation for this command before using it. Try it out with for /? and dir /? in a command prompt window and run also cmd /? because of this executable processes a batch file.
There is the executable %SystemRoot%\System32\robocopy.exe for copying an entire directory tree or deprecated older executable %SystemRoot%\System32\xcopy.exe. See Windows Commands and SS64.com - A-Z index of the Windows CMD command line for documentation of these two external commands in addition to running them in a command prompt window with /? for a brief help.

Why does execution of batch script end unexpected on IF/ELSE statement?

When the script gets to the IF statement, it just ends. It doesn't go to the next line which is pause for debugging.
set yymm=%DATE:~12,2%%DATE:~4,2%
set DD=%DATE:~7,2%
robocopy "\\client system\Users\login name\Videos" "F:\Temporary\Videos\Process\New Batch\%yymm%%dd%\Netbook\Videos" /mir
set /p %user%=Did Netbook Videos complete? (y/n):
IF %user%=="y" (del "\\client system\Users\login name\Videos\"*.* /s/q) ELSE (echo Skipping)
I know that there is a /move switch for robocopy command. But it tells me that it doesn't have access to the destination folder. The batch program runs with administrative access and it is running in the profile that created the folder. So I wrote a workaround.
Why is this happening?
I recommend first to read following answers:
Debugging a batch file explains how to debug a batch file because of cmd.exe always outputs an error message on exiting execution of a batch file because of a syntax error as caused by your wrong code.
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
The answer on this question explains how to define and use environment variables correct on Windows.
How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?
The answer on this question explains in detail with examples the usage of set /P and choice whereby the latter is better for all user prompts on which the user must take one of the options the batch file offers during execution.
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
The question is really self-explaining.
The batch file below assumes that the string substitutions done with value of dynamic environment variable DATE works with used user account because of date format depends on which region/country/locale is set for used user account.
set "yymm=%DATE:~12,2%%DATE:~4,2%"
set "DD=%DATE:~7,2%"
%SystemRoot%\System32\robocopy.exe "\\client system\Users\login name\Videos" "F:\Temporary\Videos\Process\New Batch\%yymm%%dd%\Netbook\Videos" /mir
%SystemRoot%\System32\choice.exe /N /M "Did Netbook Videos complete? (y/n): "
if errorlevel 2 (echo Skipping) else del /S /Q "\\client system\Users\login name\Videos\*"
I suggest also reading How to delete files/subfolders in a specific directory at command prompt in Windows? The command DEL as used here does not delete all files and leaves behind subdirectories which are most likely empty after deleting most or by chance all video files. But it would be good to avoid deletion of hidden system file desktop.ini in videos directory of a user account which is usually referenced with %USERPROFILE%\Videos.
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.
choice /?
del /?
echo /?
if /?
robocopy /?
set /?

Why does command START used in a batch file not start a batch file?

I made a Main batch file with the lines below:
#echo off
color 1e
title ------ Just a Test ------
start "C:\Users\%USERNAME%\Desktop\Check.bat"
:START
echo Welcome to the Game!
...
And Check.bat contains:
#echo off
if not exist "C:\Users\%USERNAME%\Desktop\Batch_System\importantFile.dll" goto ERROR
if exist "C:\Users\%USERNAME%\Desktop\Batch_System\importantFile.dll" goto CONTINUE
:ERROR
cls
echo ERROR :
echo Important file not found. please reinstall the program
pause
exit /b
:CONTINUE
cls
exit /b
When I use the command start, it starts only a command prompt with the Check.bat directory and the main batch file continues executing the game. I want to force close the main batch file if importantFile.dll doesn't exist.
Okay, let me explain: When the main batch file is executed and runs the command start to start another batch file called Check.bat, the file Check.bat checks if the file importantFile.dll exists, and if not, Check.bat displays an error message.
Does anyone know how to write Check.bat in a manner that when the .dll file does not exist, force the main batch file to exit?
First, help on every command can be get by running in a command prompt window the command with /? as parameter. start /? outputs the help of command START. call /? outputs the help of command CALL usually used to run a batch file from within a batch file. Those two commands can be used to run a batch file as explained in detail in answer on How to call a batch file that is one level up from the current directory?
Second, the command line
start "C:\Users\%USERNAME%\Desktop\Check.bat"
starts a new command process in foreground with a console window with full qualified batch file name as window title displayed in title bar at top of the console window. That is obviously not wanted by you.
Third, the Wikipedia article Windows Environment Variables lists the predefined environment variables on Windows and their default values depending on version of Windows.
In general it is better to use "%USERPROFILE%\Desktop" instead of "C:\Users\%USERNAME%\Desktop".
There is no C:\Users on Windows prior Windows Vista and Windows Server 2008 by default at all.
The users profile directory can be on a different drive than drive C:.
It is also possible that just the current user's profile directory is not in C:\Users, for example on a Windows server on which many users can logon directly and for which the server administrator decided to have the users' profile directories on a different drive than system drive making backup and cleaning operations on server much easier and is also better for security.
Well, it is also possible to have the user's desktop folder not in the user's profile directory. But that is really, really uncommon.
Fourth, on shipping a set of batch files, it is recommended to use %~dp0 to call other batch files from within a batch file because of this string referencing drive and path of argument 0 expands to full path of currently executed batch file.
The batch file path referenced with %~dp0 always ends with a backslash. Therefore concatenate %~dp0 always without an additional backslash with another batch file name, folder or file name.
See also What is the reason for batch file path referenced with %~dp0 sometimes changes on changing directory?
Fifth, I suggest following for your two batch files:
Main.bat:
#echo off
color 1e
title ------ Just a Test ------
call "%~dp0Check.bat" || color && exit /B
echo Welcome to the Game!
Check.bat:
#echo off
cls
if exist "%~dp0Batch_System\importantFile.dll" exit /B 0
echo ERROR:
echo Important file not found. Please reinstall the program.
echo/
pause
exit /B 1
The batch file Check.bat is exited explicitly on important file existing with returning exit code 0 to the parent batch file Main.bat. For that reason Windows command processor continues execution of Main.bat on the command line below the command line calling the batch file Check.bat.
Otherwise Check.bat outputs an error message, waits for a pressed key by the user and exits explicitly with non zero exit code 1. The non zero exit code results in Main.bat in executing the next command after || which is COLOR to restore initial colors and next executing also EXIT with option /B to exit the execution of Main.bat.
See also:
Single line with multiple commands using Windows batch file
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
Where does GOTO :EOF return to?
exit /B without an additionally specified exit code is like goto :EOF.
The CALL command line in Main.bat could be also written as:
call "%~dp0Check.bat" || ( color & exit /B )
And Main.bat could be also written as:
#echo off
color 1e
title ------ Just a Test ------
call "%~dp0Check.bat"
if errorlevel 1 (
color
goto :EOF
)
echo Welcome to the Game!
I do not recommend using in Main.bat just EXIT instead of exit /B or goto :EOF. Just EXIT would result in exiting the current command process independent on calling hierarchy and independent on how the command process was started: with option /K to keep it running to see error messages like on opening a command prompt window and next running a batch file from within command prompt window, or with /C to close the command process after application/command/script execution finished like on double clicking on a batch file.
It is advisable to test batch files by running them from within an opened command prompt window instead of double clicking on them to see error messages on syntax errors output by cmd.exe. For that reason usage of just EXIT is counter-productive for a batch file in development. Run cmd /? in a command prompt window for help on Windows command processor itself.
Last but not least see:
Microsoft's command-line reference
SS64.com - A-Z index of the Windows CMD command line
start is asynchronous by default. Use start /wait so that main.bat can test the exit code of check.bat. Make check.bat return an appropriate exit code.
For example...
main.bat
#echo off
start /b /wait check.bat
if not %errorlevel% == 0 exit /b
echo "Welcome to the game!"
...
check.bat
#echo off
if exist "importantfile.dll" exit 0
echo ERROR: Important file not found. Please reinstall the program.
pause
exit 1
notes
Added /b to start to avoid opening another window. Change that per your preference.
You could use call instead of start but call gives the called code access to the variables of main.bat so encapsulation is improved if you use start as you did.
The logic in check.bat is simplified above. Once you identify the success path early in the script and exit, the rest of the script can assume the fail path. This saves you a few if's and labels which you might find simplifies writing and reading of similar scripts. Beware of potentially confusing multiple exit points in longer scripts though!
When choosing exit codes, 0 is a common convention for success.
The above code is just one technique - there are several other options (such as checksomething && dosomethingifok). Some useful information on return codes, and checking them, can be found in http://steve-jansen.github.io/guides/windows-batch-scripting/part-3-return-codes.html
Thanks to the answer from Mofi. I've my example and exp. on this. To be short, it's about the setting of log date format. You may change the format of time , date and log. you may have the result.
why-batch-file-run-with-failure-in-windows-server

Command Prompt read system output

Hi Stackoverflow Community,
After many occasions of fixing computers using maintenance and integrity commands via command prompt manually I've decide I wanted to create a batch file doing the same techniques based upon command output. In terms of writing the batch file the only issue I'm struggling with it reading in data from command itself rather than text file. For example;
If Chkdsk comes back saying there multiple bad sectors, using if clause or another syntax it will schedule a repair i.e chkdsk /r next restart etc etc. The picture below with red highlighted are sections in which I wanted to read in if possible.
#echo off
chkdsk
/* If an issue been has found */
chkdsk /f
Because you want to search for two strings, you either have to do an unsane amount of code or just parse the output twice. Use a temporary file for that to not have to run the chkdsk command twice:
chkdsk d: >out.txt
type out.txt
find "No further action is required" out.txt >nul && find " 0 KB in bad sectors" out.txt >nul && goto :good
REM repair
:good
echo nothing to do

Have a command within a batch wait to run

I have a batch script and of the many commands, etc, here's one that I need help with:
findstr /i /v /%HomeDrive%"Registered Owner" %HomeDrive%\%HomePath%\Document\FOLDERNAMEHERE\systeminfo1.txt >%HomeDrive%\%HomePath%\Documents\FOLDERNAMEHERE\systeminfo.txt
Essentially what this does for me is it removes the Registered Owner field from a sysinfo log, and then dumps the newly created sysinfo log without Registered Owner. My problem is that given this is a batch script, it's going too fast and it does not allow enough time for any of the information to be written to the second .txt log. It's created with 0KB's and is blank.
I've tried implementing a line after the above code:
timeout /t 15 /nobreak >NUL
However, this does me no good as it does it after the above code is run, not while.
Can anyone please inform me how to give the file time to be written to, so the batch just doesn't go right to the next line of code in the batch leaving me with a 0KB .txt?
You have to correct your findstr command. /c:"string" is the way to indicate a search string parameter, and c: has no relation with any drive. See findstr /? for a complete reference of allowed switches.
For the wait, you have to wait before the findstr, waiting for finish of the process that generates the data.
Or, if the log is being generated with a redirection (command > file.txt) to the output file, then you can pipe the data to the findstr. Something like
whateverRetrivesData | findstr /i /v /c:"Registered Owner" > somewhere\file.txt

Resources