Parse Modified Date & Time - batch-file

I have a batch script use to display a list of files's modified date & time.
This is the script's code :
for %%v in (*.html) do (
echo %%~tv
)
Question is, how do I parse the result into two variables. Each for date and time consecutively.
Please advice.
Regards,
Dino

If I were to solve such a problem for myself, I would do the following:
I would run that script of yours to see what output it produces. To be honest, I have run that script, and here's what I've got:
12/15/2009 08:54 AM
12/15/2009 09:30 AM
05/31/2011 07:35 PM
12/02/2009 05:53 PM
12/19/2009 09:33 PM
04/10/2010 02:07 PM
11/23/2010 03:21 PM
01/06/2010 12:03 PM
My next step then would be to determine (visually) what part of every string is the date and what part is the time, I mean, what position the substrings start at and what their lengths are.
In my case it appears to be... (where's my ruler?)
1 1
0 5 0 5
||||||||||||||||||||
04/10/2010 02:07 PM
Ah yes, position 0, length 10 for the date and position 11, length 8 for the time. (As you can see, for strings in batch scripts I have a special ruler that starts from 0.)
Now that I know where to locate the date and the time in the output, I can extract them accordingly. But first I'd need a variable for the whole string, because extracting is applied to environment variables, not loop variables. Here:
SETLOCAL EnableDelayedExpansion
FOR %%v IN (*.html) DO (
SET datetime=%%~tv
ECHO !datetime:~0,10!
ECHO !datetime:~11,8!
)
ENDLOCAL
You might have noticed that apart from introducing a variable I also added enabling delayed expansion (of variables). This is necessary, because with the immediate expansion, i.e. like this:
FOR %%v IN (*.html) DO (
SET datetime=%%~tv
ECHO %datetime:~0,10%
ECHO %datetime:~11,8%
)
it wouldn't work. The thing is, the %datetime:…% expressions would be evaluated before the loop was invoked, and thus would produce empty strings. (This peculiarity of behaviour applies to all cases where you have a bunch of commands in parentheses, whether it is a FOR loop, an IF or IF..ELSE command, or even simply a redirection applied to a set of commands, like >file (commands).)
Instead of just outputting the values you could store them in other variables to process later in the loop body:
SETLOCAL EnableDelayedExpansion
FOR %%v IN (*.html) DO (
SET datetime=%%~tv
SET fdate=!datetime:~0,10!
SET ftime=!datetime:~11,8!
:: now do whatever you like with !fdate! and !ftime!
…
)
ENDLOCAL

Related

How to safely echo FOR variable %%~p followed by a string literal

I have a variable %%p created from for /f command
When i try to use it with some additional references like: %%~dp and then write some text afterwards it accesses a different variable
set var="%%~dpabc.txt"
Code outputs
%%~dpa instead of %%~dp
So you must be using FOR /F with multiple tokens, like
for /f "tokens=1-16" %%a in (file) do echo %%~dpabc.txt
Or your code could have nested FOR loops. Something like
for %%a in (something) do (
for %%p in (somethingelse) do (
echo %%~dpabc.txt
)
)
Or even something like
for %%a in (something) do call :sub
exit /b
:sub
for %%p in (somethingelse) do echo %%~dpabc.txt
exit /b
All three code examples above will print out the drive and path of %%~dpa, followed by "bc.txt". As per the documentation, the FOR variables are global, so the DO clause of the subroutine FOR loop has access to both %%a and %%p.
Aschipfl does a good job documenting the rules for how modifiers and variable letters are parsed.
Whenever you use a FOR variable before a string literal, you must be extremely careful that the string literal cannot be interpreted as part of the FOR variable expansion. As can be seen with your example, this can be difficult. Make the literal dynamic, and the problem is even worse.
set /p "myFile=Enter a file name: "
for %%a in (something) do (
for %%p in (somethingelse) do (
echo %%~dp%myFile%
)
)
If the user enters "abc.txt" then we are right back where we started. But looking at the code it is not obvious that you have a potential problem.
As Gerhard and Mofi say, you are safe if you use a character that cannot be interpreted as a modifier. But that is not always easy, especially if you are using FOR /F returning multiple tokens.
There are solutions!
1) Stop the FOR variable parsing with !! and delayed expansion
If you look at the rules for how cmd.exe parses scripts, you will see that FOR variables are expanded in phase 4 before delayed expansion occurs in phase 5. This provides the opportunity to use !! as a hard stop for the FOR expansion, provided that delayed expansion is enabled.
setlocal enableDelayedExpansion
for %%a in (something) do (
for %%p in (somethingelse) do (
echo %%~dp!!abc.txt
)
)
The %%~dp is expanded properly in phase 4, and then in phase 5 !! is expanded to nothing, yielding your desired result of the drive letter followed by "abc.txt".
But this does not solve all situations. It is possible for ! to be used as a FOR variable, but that should be easy to avoid except under extreme situations.
More troubling is the fact that delayed expansion must be enabled. This is not an issue here, but if the FOR variable expands to a string containing ! then that character will be parsed by delayed expansion, and the results will most likely be messed up.
So the !! delayed expansion hack is safe to use only if you know that your FOR variable value does not contain !.
2) Use intermediate environment variables
The only simple foolproof method to avoid problems in all situations is to transfer the value of the FOR variable to an intermediate environment variable, and then toggle delayed expansion and work with the entire desired string.
for %%a in (something) do (
for %%p in (somethingelse) do (
set "drive=%%~dp"
setlocal enableDelayedExpansion
echo !drive!abc.txt
endlocal
)
)
3) Use Unicode characters via environment variables
There is a complex bullet proof solution, but it takes a good bit of background information before you can understand how it works.
The cmd.exe command processor represents all strings internally as Unicode, as are environment variables - Any Unicode code point other than 0x00 can be used. This also applies to FOR variable characters. The sequence of FOR variable characters is based on the numeric value of the Unicode code point.
But cmd.exe code, either from a batch script, or else typed into the command prompt, is restricted to characters supported by the active code page. That might seem like a dead end - what good are Unicode characters if you cannot access them with your code?
Well there is a simple, though non-intuitive solution: cmd.exe can work with predefined environment variable values that contain Unicode values outside the active code page!
All FOR variable modifiers are ASCII characters that are within the first 128 Unicode code points. So if you define variables named $1 through $n to contain a contiguous range of Unicode characters starting with say code point 256 (0x100), then you are guaranteed that your FOR variable can never be confused with a modifier.
So if $1 contains code point 0x100, then you would refer to the FOR variable as %%%$1%. And you can freely use modifiers like `%%~dp%$1%.
This strategy has an added benefit in that it is relatively easy to keep track of FOR variables when parsing a range of tokens with something like "tokens=1-30" because the variable names are inherently sequential. The active code page character sequencing usually does not match the sequence of the Unicode code points, which makes it difficult to access all 30 tokens unless you use the Unicode variable hack.
Now defining the $n variables with Unicode code points is not a trivial development effort. Thankfully it has already been done :-) Below is some code that demonstrates how to define and use the $n variables.
#echo off
setlocal disableDelayedExpansion
call :defineForChars 1
for /f "tokens=1-16" %%%$1% in (file) do echo %%~d%$16%abc.txt
exit /b
:defineForChars Count
::
:: Defines variables to be used as FOR /F tokens, from $1 to $n, where n = Count*256
:: Also defines $max = Count*256.
:: No other variables are defined or tampered with.
::
:: Once defined, the variables are very useful for parsing lines with many tokens, as
:: the values are guaranteed to be contiguous within the FOR /F mapping scheme.
::
:: For example, you can use $1 as a FOR variable by using %%%$1%.
::
:: FOR /F "TOKENS=1-31" %%%$1% IN (....) DO ...
::
:: %%%$1% = token 1, %%%$2% = token 2, ... %%%$31% = token 31
::
:: This routine never uses SETLOCAL, and works regardless whether delayed expansion
:: is enabled or disabled.
::
:: Three temporary files are created and deleted in the %TEMP% folder, and the active
:: code page is temporarily set to 65001, and then restored to the starting value
:: before returning. Once defined, the $n variables can be used with any code page.
::
for /f "tokens=2 delims=:." %%P in ('chcp') do call :DefineForCharsInternal %1
exit /b
:defineForCharsInternal
set /a $max=%1*256
>"%temp%\forVariables.%~1.hex.txt" (
echo FF FE
for %%H in (
"0 1 2 3 4 5 6 7 8 9 A B C D E F"
) do for /l %%N in (1 1 %~1) do for %%A in (%%~H) do for %%B in (%%~H) do (
echo %%A%%B 0%%N 0D 00 0A 00
)
)
>nul certutil.exe -decodehex -f "%temp%\forVariables.%~1.hex.txt" "%temp%\forVariables.%~1.utf-16le.bom.txt"
>nul chcp 65001
>"%temp%\forVariables.%~1.utf8.txt" type "%temp%\forVariables.%~1.utf-16le.bom.txt"
<"%temp%\forVariables.%~1.utf8.txt" (for /l %%N in (1 1 %$max%) do set /p "$%%N=")
for %%. in (dummy) do >nul chcp %%P
del "%temp%\forVariables.%~1.*.txt"
exit /b
The :defineForChars routine was developed at DosTips as part of a larger group effort to easily access many tokens with a FOR /F statement.
The :defineForChars routine and variants are introduced in the following posts within that thread:
Work with a fixed number of columns
Work with varying numbers of columns
Dynamically choose which tokens to expand within the DO clause
This behaviour is caused by the kind of greedy nature of the parsing of for variable references and its ~-modifiers. Basically it follows these rules, given the preceding %/%%-signs have already been detected:
if Command Extensions are enabled (default), check if next character is ~; if yes, then:
take as many as possible of the following characters in the case-insensitive set fdpnxsatz (even multiple times each) that are preceding a character that defines a for variable reference or a $-sign; if such a $-sign is encountered, then:
scan for a :1; if found, then:
if there is a character other than % following the :, use it as a for variable reference and expand as expected, unless it is not defined, then do not expand;
if the : is the last character, cmd.exe will crash!
else (no : is found) do not expand anything;
else (if no $-sign is encountered) expand the for variable using all the modifiers;
else (if no ~ is found or Command Extensions are disabled) use the next character as a for variable reference, except it is a %-sign2, and expand, unless such is not defined, or there is not even a character following, then do not expand;
1) The string between $ and : is considered as the name of an environment variable, which may even be empty; since an environment variable cannot have an empty name, the behaviour is just the same as for an undefined environment variable.
2) A %-sign can be the name of a for meta-variable, but it cannot be expanded without a ~-modifier.
This answer has meanwhile been posted in an augmented manner as a community answer to the thread How does the Windows Command Interpreter (CMD.EXE) parse scripts?.
As already explained in the for meta-variable parsing rules, the ~-modifier detection happens in a greedy manner. But you can stop parsing by another for meta-variable, which eventually expands to nothing, or by the ~$-modifier as suggested by jeb in a comment, which does not even require another for meta-variable, so any existing one can be used:
rem // Using `%%~#` will expand to an empty string (note that `#` is not a valid `~`-modifier):
for %%# in ("") do (
rem // Establish a `for`-loop that defines meta-variables `%%a` to `%%p`:
for /F "tokens=1-16" %%a in ("1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16") do (
rem /* Since `d`, `p` and `a` are all valid `~`-modifiers and a `for` meta-variable
rem `%%b` exists while `b` is not a valid `~`-modifier, `%%~dpab` is expanded: */
echo(%%~dpabc.txt
rem /* `for` meta-variable parsing is stopped after `%%~dp`, because the following `%`
rem is not a valid `~`-modifier, and neither exists a `for` meta-variable named `%`;
rem `%%~#` is expanded to an empty sting then (parsing surely stops at `#`): */
echo(%%~dp%%~#abc.txt
rem /* The following does not even require a particular `for` meta-variable like `%%#`,
rem it just uses the existing one `%%p` with the `~$`-modifier that specifies an
rem environment variable; since there is no variable name in between `$` and `:`,
rem there is certainly no such variable (since they must have names), hence `$~%:p`
rem expands to an empty string; note that `~$` is always the very last modifier: */
echo(%%~dp%%~$:pabc.txt
)
)
Note that this approach fails in case there is a for meta-variable named % (which is not quite common but possible).

Arithmetic operations with HH:MM:SS times in batch file

In one of my batch scripts I need to calculate the duration of an interval in a video file. First the user is asked to input the start and end times:
set /p StartPosition=Start position (HH:MM:SS):
set /p EndPosition=End position (HH:MM:SS):
Then, I would like the batch script to calculate the duration in between.
How can I subtract %StartPosition% from %EndPosition% like this, for example:
00:10:40 - 00:10:30 = 00:00:10
The reason why I can't figure out how to do this is because these numbers are separated by colons.
Edit: This question is different to this question because I do not need the scrip to treat the numbers as time values.
#echo off
setlocal
set /p "StartPosition=Start position (HH:MM:SS): "
set /p "EndPosition=End position (HH:MM:SS): "
set /A "ss=(((1%EndPosition::=-100)*60+1%-100)-(((1%StartPosition::=-100)*60+1%-100)"
set /A "hh=ss/3600+100,ss%%=3600,mm=ss/60+100,ss=ss%%60+100"
echo Duration=%hh:~1%:%mm:~1%:%ss:~1%
EDIT: Some explanations added
This program use the usual method to convert a time in HH:MM:SS format into a number of seconds via the standard formula: seconds = (HH*60+MM)*60+SS. However, the set /A command consider the numbers that start with 0 as written in octal base, and hence 08 and 09 would be invalid octal numbers. To avoid this problem, a digit 1 is placed before expand the number and a 100 is subtracted after, so if HH=08 then 1%HH%-100 correctly gives 8; that is:
set /A seconds = ((1%HH%-100)*60+1%MM%-100)*60+1%SS%-100
There are several methods to split a time given in HH:MM:SS format into its three parts. For example, if we take set EndPosition=HH:MM:SS as base, then we may use a for /F command this way:
for /F "tokens=1-3 delims=:" %%a in ("%EndPosition%") do (
set /A "seconds=((1%%a-100)*60+1%%b-100)*60+1%%c-100"
)
In this program a different method is used. If we match the original EndPosition=HH:MM:SS string with the desired formula, we may construct this mapping scheme:
HH : MM : SS
((1 HH -100)*60+1 MM -100)*60+1 SS -100
In other words: if we replace the colons of the original string by -100)*60+1 and insert ((1 at beginning and -100 at end, we obtain the desired formula; that is:
set /A "seconds=((1%EndPosition::=-100)*60+1%-100"
This is a very efficient method that even allows to replace both EndPosition and StartPosition strings in the same formula (enclosing both parts in parentheses) and directly subtract them:
set /A "ss=(((1%EndPosition::=-100)*60+1%-100)-(((1%StartPosition::=-100)*60+1%-100)"
You may cancel the #echo off command and run the program to review the exact formula that is evaluated after the values of the variables are replaced. For example, when StartPosition=00:10:30 and EndPosition=00:10:40, this is the expression that is evaluated:
set /A "ss=(((100-100)*60+110-100)*60+140-100)-(((100-100)*60+110-100)*60+130-100)"
Just to complete this description, this is the "standard" way to evaluate the same formula using a for /F command:
for /F "tokens=1-6 delims=:" %%a in ("%EndPosition%:%StartPosition%") do (
set /A "ss=(((1%%a-100)*60+1%%b-100)*60+1%%c-100)-(((1%%d-100)*60+1%%e-100)*60+1%%f-100)"
)
The opposite conversion from number of seconds to HH:MM:SS parts is straightforward:
HH=SS/3600, rest=SS%3600, MM=rest/60, SS=rest%60
However, each part in the result must be displayed with two digits, but this formatting may be achieved in a very simple way. Instead of insert three if commands that check if each part is less than 10 and insert a padding zero in such a case, the number 100 is just added to the parts (converting an 8 into 108, for example), and when each part is displayed the first digit is omitted (so just 08 is shown). This is a very efficient method to format numbers that may be performed in the same set /A command used to obtain the parts. For example:
set /A "hh=ss/3600+100,ss%%=3600,mm=ss/60+100,ss=ss%%60+100"
echo Duration=%hh:~1%:%mm:~1%:%ss:~1%
In this way, the conversion of two times into two number of seconds, their subtraction and the opposite conversion and formatting to HH:MM:SS is performed in two SET /A commands, that even may be written in a single, long line.
Output examples:
Start position (HH:MM:SS): 00:10:30
End position (HH:MM:SS): 00:10:40
Duration=00:00:10
Start position (HH:MM:SS): 00:10:45
End position (HH:MM:SS): 00:11:05
Duration=00:00:20
This is possible to do in pure batch by parsing each field as an independent string, then doing arithmetic on them. Many practical solutions call into some other program to do the date math.
The following code calls into PowerShell to use the .NET DateTime class to do the parsing for you.
C:\> set "StartPosition=00:10:30"
C:\> set "EndPosition=00:10:40"
C:\> PowerShell.exe -c "$span=([datetime]'%EndPosition%' - [datetime]'%StartPosition%'); '{0:00}:{1:00}:{2:00}' -f $span.Hours, $span.Minutes, $span.Seconds"
00:00:10
This executes two lines of PowerShell code; one to convert both times into DateTime objects and subtract them, and the other to output the result in the format you specified.
Here's a working prototype:
#echo off
set T1=00:10:45
set T2=00:11:05
set HOUR1=%T1:~,2%
set MIN1=%T1:~3,-3%
set SEC1=%T1:~-2%
set HOUR2=%T2:~,2%
set MIN2=%T2:~3,-3%
set SEC2=%T2:~-2%
set /A TOTAL_SEC1=%HOUR1%*3600+%MIN1%*60+SEC1
set /A TOTAL_SEC2=%HOUR2%*3600+%MIN2%*60+SEC2
set /A DIFF=%TOTAL_SEC2%-%TOTAL_SEC1%
echo %DIFF%
Output:
20
Its not complete, but its a reasonable start.
I think, #Aacini has cleared Everything here. He got you, Before I Do. But, I want to Improved on him as - by using For Loop to make code Easier.
Note: Everything after 'REM' is a Comment for the sake of understanding easily...
All You need to DO is Copy It into Your Batch File. And, Use it as follows (in your main code):
Syntax: Call :Time [Your Time 1] [Operation] [Your Time 2]
And, You can Now apply - any operation - including 'Addition, Substraction, Division, Multiplication' ;)
The Time Function
--------------Copy the Below Code----------
:Time [Start_Time] [Operation] [End_Time]
SetLocal EnableDelayedExpansion
REM Creating a portable Function for your Job. :)
REM Reading Start-time...
For /F "Tokens=1,2,3 Delims=:" %%A in ("%~1") Do (
Set _Start_Hour=%%A
Set _Start_Min=%%B
Set _Start_Sec=%%C
)
REM Reading End-time...
For /F "Tokens=1,2,3 Delims=:" %%A in ("%~3") Do (
Set _End_Hour=%%A
Set _End_Min=%%B
Set _End_Sec=%%C
)
REM Removing leading Zero's - if any... 'CMD assumes it as octal - otherwise'
For %%A In (Hour Min Sec) Do (
For %%B In (Start End) Do (
IF /I "!_%%B_%%A:~0,1!" == "0" (Set _%%B_%%A=!_%%B_%%A:~1!)
)
)
REM Applying Operation on the given times.
For %%A In (Hour Min Sec) Do (Set /A _Final_%%A=!_Start_%%A! %~2 !_End_%%A!)
REM Handling a little Exceptional errors! - due to the nature of time (60 sec for a min.)
SET _Extra_Hour=0
SET _Extra_Min=0
REM Two Cases can arise in each part of time...
:Sec_loop
IF %_Final_Sec% GTR 59 (Set /A _Extra_Min+=1 & Set /A _Final_Sec-=60 & Goto :Sec_loop)
IF %_Final_Sec% LSS 0 (Set /A _Extra_Min-=1 & Set /A _Final_Sec+=60 & Goto :Sec_loop)
Set /A _Final_Min+=%_Extra_Min%
:Min_loop
IF %_Final_Min% GTR 59 (Set /A _Extra_Hour+=1 & Set /A _Final_Min-=60 & Goto :Min_loop)
IF %_Final_Min% LSS 0 (Set /A _Extra_Hour-=1 & Set /A _Final_Min+=60 & Goto :Min_loop)
Set /A _Final_Hour+=%_Extra_Hour%
REM Saving Everything into a Single Variable - string.
Set _Final_Time=%_Final_Hour%:%_Final_Min%:%_Final_Sec%
REM Displaying it on the console. ;)
Echo.%_Final_Time%
Goto :EOF
--------------End OF Code----------------------
You can Also visit, my Website - based on Batch Programming. (www.thebateam.org) You'll find alot of stuff there - to help you out. :)
Here's the Final Output - When I saved the Code in Answer.bat File
To offer a concise alternative to Ryan Bemrose's helpful, PowerShell-based answer:
:: Sample variable values.
set "StartPosition=00:10:30"
set "EndPosition=00:10:40"
:: Use PowerShell to perform the calculation,
:: using the .NET System.Timespan ([timespan]) type.
powershell -c \"$([timespan] '%EndPosition%' - '%StartPosition%')\"
Yes, you pay a performance penalty for invoking the PowerShell CLI, but I invite you to compare this solution to Aacini's clever, but highly obscure batch-language-only solution in terms of readability and conceptual complexity.
Generally speaking:
cmd.exe is a shell and, historically, shells have provided very limited language capabilities themselves, as their focus was on calling built-in or external commands.
cmd.exe's language, as used in batch files (.cmd, .bat) is very limited, and saddled with many counterintuitive behaviors that can't be fixed so as not to break backward compatibility.
Over the decades, users have learned to stretch the language to its limits, coming up with many clever techniques to squeeze more functionality out of it. While helpful if you're stuck on pre-PowerShell systems (virtually extinct at this point) or you must use batch files and performance is paramount (rarely the case), the obscurity of these techniques makes them both hard to understand and to remember.
cmd.exe's successor, PowerShell, with its .ps1 scripts, offers a far superior language that offers virtually unlimited access to .NET functionality and COM.
PowerShell too has its fair share of counterintuitive behaviors that can't be fixed, but, by and large, it is a far more capable and predictable language than the batch language; some of its undeniable, but unavoidable complexity comes from having to talk to multiple worlds (.NET, COM, WMI) while still also acting as a shell (with respect to calling external programs and the shell-like syntax of its built-in command as well as user-defined ones).
Here, the batch file uses a call an external program, powershell.exe, the PowerShell CLI, to delegate the task at hand to its superior language.
Calling the PowerShell CLI is expensive in terms of performance, but offers a way to perform tasks that batch files either cannot, or can only do with much more effort and/or highly obscure techniques.
Of course, needing to "speak" both the batch language and PowerShell to implement a given task adds complexity of its own, so the logical progression is to implement the entire task in PowerShell (in a .ps1 script).
Unfortunately, PowerShell puts up some road blocks here, in the name of security:
In workstation editions of Windows, execution of scripts is disabled by default, and requires a one-time call such as the following to enable it (see this answer for background information):
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
.ps1 scripts - unlike batch files - cannot be executed directly from outside PowerShell, notably not by double-clicking in File Explorer or from a cmd.exe session.
From inside a PowerShell session that is not a concern, but if you do need this capability in a given scenario, a simple workaround is to create a companion batch file with the same base file name as the .ps1 script (e.g., foo.cmd in the same directory as the target PowerShell script, foo.ps1, with the following, generic content:
#powershell -noprofile -file "%~dpn0.ps1"

Command Line: Using a for loop to create directories with increasing names

I am trying to write a batch file that does the following:
Prompt user for the directory to create the new folder newest
Prompt user for an integer limit
Create directory newest
CD newest
FOR loop for limit iterations
Create directory "Month " + iteration
For example:
newest = Reports
limit = 12
I should end up with:
\Reports\Month 1
\Reports\Month 2
\Reports\Month 3
...
\Reports\Month 12
This is my code so far:
setlocal enabledelayedexpansion
FOR /L %%i IN (1,1,%limit%) DO (
set "month_counter=Month %%i"
echo %month_counter%
MD %month_counter%
)
endlocal
If I set limit = 12, I get 12 error messages stating:
Echo is off.
The syntax of the command is incorrect.
I appreciate the help.
FOR /L %%i IN (1,1,%limit%) DO (
MD "Month %%i"
)
You have the standard delayed expansion problem - hundreds of articles on SO about this.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var. CALL ECHO %%errorlevel%% displays, but sadly then RESETS errorlevel.
So - you could use
set "month_counter=Month %%i"
CALL echo %%month_counter%%
If you really, really want to - or one of the other techniques, but it's far easier to simply make the directory from your constant data + the iteration counter in %%i as shown.

Batch file command for creating a file's name as per the current date

Sorry to ask the below question command but I am stuck by its simplicity.
I need a windows batch file which creates a text file having the name of the today date, as per the computer date.
10th July 2014 >>> 10072014 >>> 10-07-2014.txt
Thanks for your help
The way I parse time in this sense varies. I adjust based on the server or workstation on which I'm placing the batch file. No, this isn't very portable in that sense but it's easy enough to adjust.
If your short-date format is mm/dd/yyyy the easy way is
SET DT=%date:/=-%
ECHO New File > %DT%.txt
This makes the "/" in the short date a "-", which is compatible with file names.
For most server applications I go the extra mile and break out the MM/DD, etc.:
SET DD=%date:~0,2%
SET MM=%date:~3,2%
SET YY=%date:~8,2%
SET YYYY=%date:~6,4%
SET DT=<your desired combo of month, day, year>
ECHO New File > %DT%.txt
At that point it's very easy for me and avoids using FOR loops that parse dates and times to work regardless of location or regional settings. Which is not important to me.
This is essentially what is in this link, provided also by foxidrive. SO has a number of options.
Following is a custom technique where it generates files with names as the date. It also contains some pre-typed data so that you can generate at the time of creation only and save your time from mundane activity.
I have set Y i.e year as 2020, M i.e month as 09 and day is auto-generated from the for loop from 1 to 31.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET PATH=D:\ABHISHEK_DUTTA\WORK\DAILY_LOGS\
SET Y=2020
SET M=09
FOR /L %%I IN (1,1,31) DO (
IF %%I GTR 9 (SET "D=%%I") ELSE (SET "D=0%%I")
SET FNAME=%PATH%%Y%%M%!D!.txt
(ECHO Notes for !D!-%M%-%Y%.
ECHO Discussions
ECHO 1.
ECHO 2.
ECHO Remark) > !FNAME!
)
It checks whether I variable is greater than 9 otherwise concatenates 0.
In FNAME i.e. the filename is concatenated with which the file is to be created.

SET command advanced /A features resource

Have done quite a bit of searching for a guide (of any substance) for the above to no avail. Can anyone refer me to one?
In the present tense however, I am trying to understand the below code example, which returns a two digit representation of the month, that corresponds to the 3 character month name set in v:
SET v=May
SET map=Jan-01;Feb-02;Mar-03;Apr-04;May-05;Jun-06;Jul-07;Aug-08;Sep-09;Oct-10;Nov-11;Dec-12
CALL SET v=%%map:*%v%-=%%
SET v=%v:;=&rem.%
ECHO.%v%
SET v=May Set the variable
SET map=Jan-01;Feb-02;Mar-03;Apr-04;May-05;Jun-06;Jul-07;Aug-08;Sep-09;Oct-10;Nov-11;Dec-12 Set the substitution string
CALL SET v=%%map:*%v%-=%% - Set %v to the map, but replace everything up to %v%- with nothing
(: replace * everything up to and including May- with nothing (no substitution code after =) - v is now 05;Jun-06;Jul-07;Aug-08;Sep-09;Oct-10;Nov-11;Dec-12
SET v=%v:;=&rem.% replace ; with &rem sets up a line that sets v to 05 and the & rem comments out all the other parts of the command. The command executed is SET v=05 & rem.Jun-06 & rem.Jul-07 & rem.Aug-08 & rem.Sep-09 & rem.Oct-10 & rem.Nov-11 & rem.Dec-12
ECHO.%v% gives the answer
This site gives a lot of info, but running the batch file, and putting echo %v% will also help
Excuse me. I don't like this type of code. In my personal opinion, it is much clear to manage these values as an array this way:
rem Prepare the array of two-digits values for each 3-letters month names:
for %%a in ("Jan=01" "Feb=02" "Mar=03" "Apr=04" "May=05" "Jun=06" "Jul=07" "Aug=08" "Sep=09" "Oct=10" "Nov=11" "Dec=12") do (
set month%%~a
)
rem Previous code is equivalent to these 12 lines:
rem set monthJan=01
rem set monthFeb=02
. . . .
rem set monthDec=12
rem This way, the result is immediately accessible:
SET v=May
CALL SET v=%%month%v%%%
rem Or in the clearer way using Delayed Expansion:
SET v=!month%v%!
monthXXX is an array of values for 12 different 3-letters month names.
SET v=May set the value of the index to an element array.
SET v=!month%v%! is first expanded to SET v=!monthMay! (normal expansion), and then to SET v=05 (Delayed Expansion).
I had explained the reasons I have to prefer this notation with every detail in these posts:
Arrays, linked lists and other data structures in cmd.exe (batch) script
DIR output into BAT array?
I apologize if someone thinks this answer is off-topic...
Antonio
For those who wants the other way around, and wanted an error if the entered month number is wrong, put this in a file named script.cmd:
#echo off
set mNum=%1
set mMap=01-Jan;02-Feb;03-Mar;04-Apr;05-May;06-Jun;07-Jul;08-Aug;09-Sep;10-Oct;11-Nov;12-Dec
call set mName=%%mMap:*%mNum%-=%%
set mName=%mName:;=&rem.%
if "%mName%" == "01-Jan" (
echo Wrong month number "%mNum%"!
goto :EOF
)
echo Month %mNum% is "%mName%".
And start the script with a parameter:
> script 02
Month 02 is "Feb".
> script 13
Wrong month number "13"!
> script foo
Wrong month number "foo"!
However, it does not cover an empty value:
> script
Month is "Jan".

Resources