1. Some general advice for writing batch files
A list of commands is output on executing in a command prompt window help
. It is advisable to use in batch files for environment variables and labels not a string which is also a command. It is possible, but not advisable.
start
is a command to start an application in a separate process. So it is better to use for example Begin
instead of start
as label.
choice
is a command for a choice which is better for single character choices than using set /P
. So it is better to use for example UserChoice
instead of just choice
as environment variable name.
It is better to use echo/
instead echo.
to output an empty line. The reason is explained by DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/.
Environment variable names and labels are easier to read on using CamelCase and can be more easily searched case-sensitive and if necessary replaced in a batch file than a name/label which can exist as word also in comments and in strings output with echo
.
The answer on question Why is no string output with 'echo %var%' after using 'set var = text' on command line? explains in detail why the usage of the syntax set "Variable=string value"
is recommended in batch files on assigning a string to an environment variable.
The directory separator on Windows is the backslash character \
. The slash character /
is the directory separator on Unix/Linux/Mac. On Windows /
is used for options/parameters. The Windows kernel functions support also directory and file paths with /
as directory separator by automatically correcting them to \
internally in path. But it is nevertheless recommended to use in a batch file \
in paths.
rem
is the command for a comment in a batch file. ::
is an invalid label and not really a comment. Lines with a label at begin are ignored for command execution. But a label cannot be used in a command block. For that reason it is recommended to use command rem
because ::
in a command block results often in unexpected behavior on execution of the batch file.
2. Get current date in a specific format
Let us look on the command line:
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set mydate=%%c-%%a-%%b)
date /t
is a command which for
executes in a background command process with the command line cmd.exe /C date /t
for capturing the output of this command process written to standard output handle STDOUT and process the captured output line by line.
Can this be optimized?
Yes, because on running in a command prompt window set /?
and reading the output help from first to last page it can be read that there is the environment variable DATE
which expands to current date. So there is no need to run the command date
to get current date as string.
The command date
with option /t
outputs the current date in the format defined for the used user account in Windows Region and Language settings. In your case it looks like the region dependent date format is MM/dd/yyyy
with the weekday abbreviation at beginning (with no comma) before the date. The date format on my computer is just dd.MM.yyyy
without weekday. The environment variable DATE
is in same region dependent format as output of command date /t
.
So the region dependent date in format ddd, MM/dd/yyyy
could be also modified to yyyy-MM-dd
using the command line:
for /F "tokens=2-4 delims=/, " %%a in ("%DATE%") do set "MyDate=%%c-%%a-%%b"
It is also possible to use string substitution:
set "MyDate=%DATE:~-4%-%DATE:~-10,2%-%DATE:~-7,2%"
String substitution is also explained by help output on running set /?
and read the answer on
What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean?
But if yyyy-MM-dd
is the wanted date format for current date independent on region settings of the used user account is advisable to use the command lines
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "MyDate=%%I"
set "MyDate=%MyDate:~0,4%-%MyDate:~4,2%-%MyDate:~6,2%"
This region independent solution is really much slower than the above command lines. It is explained in detail by the answer on Why does %date% produce a different result in batch file executed as scheduled task? But it has the big advantage of being region independent.
3. Prompting user for a single character choice
The usage of set /P variable=prompt
is not recommended for a single character choice because
- the user can just hit RETURN or ENTER without entering anything at all resulting in
variable
keeping its current value or still not being defined if not defined before set /P
command line;
- the user can make a typing mistake and presses for example Shift+2 instead of just 2 resulting (on German keyboard) to enter
"
as string which most batch files using set /P
breaks because of a syntax error on next command line evaluating the user input;
- the user can enter anything instead of one of the characters asked for including strings which on next command line results in deletion of files and folders.
The solution is using the command choice
if that is possible (depends on Windows version). choice
waits for the key press of a character specified in the command options and immediately continues after one of these keys is pressed. And choice
exits with the index of the pressed character in list as specified in batch file. This exit code is assigned to ERRORLEVEL
which can be evaluated next also within a command block without using delayed expansion or used directly in a single goto
instruction.
4. Rewritten batch file
Here is the rewritten batch file:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem set "Folder=C:\some folder"
set "Folder=F:\Temp\Test"
:Prepare
cls
rem Get current date region independent in format yyyy-MM-dd.
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "MyDate=%%I"
set "MyDate=%MyDate:~0,4%-%MyDate:~4,2%-%MyDate:~6,2%"
set "FileNumber=0"
for %%I in ("%Folder%\file-*.ext") do call :GetFileNumber "%%~nI"
goto IncrementNumber
rem Subroutine to find out highest file number without using delayed
rem environment variable expansion for number range 0 to 2147483647.
rem Numbers starting with 0 are interpreted as octal number in number
rem comparison which makes it necessary to remove leading 0 from the
rem number string get from file name starting with 5 characters.
:GetFileNumber
set "Number=%~1"
set "Number=%Number:~5%
:RemoveLeadingZero
if "%Number%" == "" goto :EOF
if "%Number:~0,1%" == "0" set "Number=%Number:~1%" & goto RemoveLeadingZero
if %Number% GTR %FileNumber% set "FileNumber=%Number%"
goto :EOF
rem Make sure the file number has at least 3 digits.
:IncrementNumber
set /A FileNumber+=1
if %FileNumber% GEQ 100 goto ExistDelivery
set "FileNumber=00%FileNumber%"
set "FileNumber=%FileNumber:~-3%"
rem Test to see if file exists already.
:ExistDelivery
if not exist "..\delivery_%MyDate%.txt" goto Begin
echo/
%SystemRoot%\System32\choice.exe /C YN /N /M "Delivery note already exists, continue (Y/N)? "
if errorlevel 2 goto :EOF
:Begin
set "FileName=file-%FileNumber%.ext"
copy "%Folder%\file.ext" "%Folder%\%FileName%" >nul
echo/
echo Choose the following:
echo/
echo 1. Directories
echo 2. Files
echo 3. Quit
echo/
%SystemRoot%\System32\choice.exe /C 123 /N /M "Your choice? "
if errorlevel 3 goto :EOF
if errorlevel 2 goto GetFileList
dir * /AD /ON /B >"..\delivery_%MyDate%.txt"
echo/
goto CheckSuccess
:GetFileList
dir * /A-D /ON /B >"..\delivery_%MyDate%.txt"
echo/
:CheckSuccess
rem More commands.
endlocal
It was not really clear for me what the entire batch code is for at all.
It would have been also easier to write the determination of highest number in a file name on knowing the possible number range like 001
to 100
. So I wrote a general solution for 001
, 002
, ..., 099
, 100
, 101
, ..., 1000
, ..., 2147483647
.
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.
call /?
cls /?
copy /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
rem /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
See also answer on Single line with multiple commands using Windows batch file for an explanation of &
operator and read the Microsoft article about Using command redirection operators.