I am trying to create an environment variable for yesterday's date. It MUST be in the format of MM/DD/YYYY.
For code I currently have:
set m=%date:~-7,2%
set /A m -= 1
set DATE_YESTERDAY=%date:~-10,2%/%m%/%date:~-4,4%
echo %DATE_YESTERDAY%
This works great, HOWEVER, when I offset to yesterdays day with -1 instead of "12/03/2019" I get "12/3/2019". Thus, missing the zero/0.
Any ideas to keep format as MM/DD/YYYY?
I have seen many other questions regarding this, however, all come short!
NOTE - I do NOT want powershell or VBS scripts. I know this can be done with simply batch.
What you should do is use a method which is not based upon user/locale/PC settings. Commonly wmic is used for that, but it isn't the fastest method and would still require performing some math.
You could use vbscript embedded directly into your batch-file:
<!-- :
#Echo Off
For /F %%# In ('CScript //NoLogo "%~f0?.wsf"') Do Set "YesterDate=%%#"
Echo(%YesterDate%
Pause
GoTo :EOF
-->
<Job><Script Language="VBScript">
dtmYesterday = DateAdd("d", -1, Now())
strDate = Right("0" & Month(dtmYesterday), 2) _
& "/" & Right("0" & Day(dtmYesterday), 2) _
& "/" & Year(dtmYesterday)
WScript.Echo strDate
</Script></Job>
You could also, if you prefer it, use powershell from your batch-file instead:
#Echo Off
For /F %%# In ('PowerShell -NoP "(Get-Date).AddDays(-1).ToString('MM/dd/yyy')"'
)Do Set "YesterDate=%%#"
Echo(%YesterDate%
Pause
Windows command processor cmd.exe does not have built-in support for date calculations. Other script interpreters installed by default on Windows like VBScript or PowerShell support date calculations and Compo posted solutions using VBScript or PowerShell.
Here is a pure batch file solution to calculate yesterday's date from current date with remarks explaining the code. The lines with remark command rem can be removed for faster processing the batch file by Windows command processor.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
if "%~1" == "" (
rem Get local date and time in a region independent format.
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS get LocalDateTime /VALUE') do set "LocalDateTime=%%I"
) else (
rem This is for fast testing determining the date of yesterday from any
rem date specified as parameter in format yyyyMMdd on calling this batch
rem file from within a command prompt window. The parameter string is
rem not validated at all as this is just for testing the code below.
set "LocalDateTime=%~1"
)
rem Get day, month and year from the local date/time string (or parameter).
set "Day=%LocalDateTime:~6,2%"
set "Month=%LocalDateTime:~4,2%"
set "Year=%LocalDateTime:~0,4%"
rem Define a variable with today's date in format MM/dd/yyyy.
set "Today=%Month%/%Day%/%Year%"
rem Decrease the day in month by 1 in any case.
rem It is necessary to remove leading 0 for the days 08 and 09 as
rem those two days would be otherwise interpreted as invalid octal
rem numbers and decreased result would be -1 instead of 7 and 8.
rem if "%Day:~0,1%" == "0" set "Day=%Day:~1%"
rem set /A Day-=1
rem Faster is concatenating character 1 with the day string to string
rem representing 101 to 131 and subtract 101 to decrease day by one.
set /A Day=1%Day%-101
rem The yesterday's date is already valid if the day of month is greater 0.
if %Day% GTR 0 goto BuildYesterday
rem Yesterday is in previous month if day is equal (or less than) 0.
rem Therefore decrease the current month by one with same method as
rem described above to decrease correct also the months 08 and 09.
set "Day=31"
set /A Month=1%Month%-101
rem Yesterday is in previous year if month is equal (or less than) 0.
if %Month% GTR 0 goto GetLastDay
set /A Year-=1
set "Month=12" & goto BuildYesterday
:GetLastDay
rem Determine last day of month depending on month.
for %%I in (4 6 9 11) do if %Month% == %%I set "Day=30" & goto BuildYesterday
if not %Month% == 2 goto BuildYesterday
rem Determine if this year is a leap year with 29 days in February.
set /A LeapYearRule1=Year %% 400
set /A LeapYearRule2=Year %% 100
set /A LeapYearRule3=Year %% 4
rem The current year is always a leap year if it can be divided by 400
rem with 0 left over (1600, 2000, 2400, ...). Otherwise if the current
rem year can be divided by 100 with 0 left over, the current year is NOT
rem a leap year (1900, 2100, 2200, 2300, 2500, ...). Otherwise the current
rem year is a leap year if the year can be divided by 4 with 0 left over.
rem Well, for the year range 1901 to 2099 just leap year rule 3 would be
rem enough and just last IF condition would be enough for this year range.
set "Day=28"
if LeapYearRule1 == 0 goto BuildYesterday
if NOT %LeapYearRule2% == 0 if %LeapYearRule3% == 0 set "Day=29"
rem The leading 0 on month and day in month could be removed and so both
rem values are defined again as string with a leading 0 added and next just
rem last two characters are kept to get day and month always with two digits.
:BuildYesterday
set "Day=0%Day%"
set "Day=%Day:~-2%"
set "Month=0%Month%"
set "Month=%Month:~-2%"
rem Define a variable with yesterday's date in format MM/dd/yyyy.
set "Yesterday=%Month%/%Day%/%Year%"
echo Today is: %Today%
echo Yesterday is: %Yesterday%
endlocal
Please read my answer on Why does %date% produce a different result in batch file executed as scheduled task? It explains in full details the FOR command line using WMIC to get current date in region independent format.
Here is the code above without comments, empty lines and first IF condition needed only for testing the code. The leap year identification is also optimized for using only third rule which means this code is working only for the years 1901 to 2099 which should be enough.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS get LocalDateTime /VALUE') do set "LocalDateTime=%%I"
set "Day=%LocalDateTime:~6,2%" & set "Month=%LocalDateTime:~4,2%" & set "Year=%LocalDateTime:~0,4%"
echo Today is: %Month%/%Day%/%Year%
set /A Day=1%Day%-101
if %Day% GTR 0 goto BuildYesterday
set "Day=31"
set /A Month=1%Month%-101
if %Month% GTR 0 goto GetLastDay
set /A Year-=1
set "Month=12" & goto BuildYesterday
:GetLastDay
for %%I in (4 6 9 11) do if %Month% == %%I set "Day=30" & goto BuildYesterday
if not %Month% == 2 goto BuildYesterday
set /A LeapYearRule3=Year %% 4
if %LeapYearRule3% == 0 (set "Day=29") else set "Day=28"
:BuildYesterday
set "Day=0%Day%"
set "Day=%Day:~-2%"
set "Month=0%Month%"
set "Month=%Month:~-2%"
echo Yesterday is: %Month%/%Day%/%Year%
endlocal
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.
echo /?
endlocal /?
for /?
goto /?
if /?
rem /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
See also single line with multiple commands using Windows batch file for an explanation of operator & used to specify more than one command on a single command line.
Trying to find the difference. But when this powershell command is inside in the findstr, it fails. On its own, it returns the correct value. Also, without the loop, it returns the correct value.
echo:!newvalue!| findstr /R "^[0123456789][0123456789]\.[0123456789]$" >nul
if errorlevel 1 (
set newvalue=
) else (
FOR /F "usebackq delims=" %%i IN (`powershell -nop -c "'{0:n1}' -f (%newvalue% - 12.0)"`) DO (SET difference=%%i)
echo %difference%
)
Can anyone figure out what I'm missing/did wrong?
Thanks in advance.
I recommend reading How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Windows command processor replaces all environment variable references using syntax %variable% inside a command block starting with ( and ending with matching ) already on parsing the command line using this command block. This means the command line echo %difference% inside ELSE branch command block of the IF command is modified by cmd.exe before command IF is executed at all. %difference% is replaced by current value of environment variable difference or an empty string in case of environment variable difference is not defined somewhere above the IF condition. In latter case echo is the command line remaining after parsing the command block and therefore shows status of command echoing instead of the string value assigned to environment variable difference in the command line above. The solution with already enabled delayed environment variable expansion is using echo !difference! in ELSE command block.
A solution for this floating point subtraction without usage of PowerShell can be seen below:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
if defined NewValue goto Validate
:UserPrompt
set /P "NewValue=Enter value between 00.0 and 99.9: "
:Validate
echo:!NewValue!| %SystemRoot%\System32\findstr.exe /R "^[0123456789][0123456789]\.[0123456789]$" >nul
if errorlevel 1 set "NewValue=" & goto UserPrompt
for /F "tokens=1,2 delims=." %%I in ("%NewValue%") do set "PreComma=%%I" & set "PostComma=%%J"
set /A Difference=1%PreComma% - 112
set "Difference=%Difference%.%PostComma%"
echo Difference is: %Difference%
endlocal
After validating that the string assigned to environment variable NewValue indeed consists of two digits, a point and one more digit as requested and expected and described at How can I do a negative regex match in batch?, the floating point number string is split up on . into pre-comma and post-comma number strings.
The pre-comma number is subtracted by 12 using an arithmetic expression. But it must be taken into account that an integer number with a leading 0 is interpreted by cmd.exe on evaluation of the arithmetic expression as octal number. That is no problem for 00 to 07. But 08 and 09 would be invalid octal numbers and so Windows command processor would use value 0 resulting in a wrong subtraction result if simply set /A Difference=PreComma - 12 would have been used in batch file. The workaround is concatenating the string 1 with the pre-comma string to a number string in range 100 to 199 and subtract 112 to get the correct result.
The post-comma value does not need to be modified and so the Difference value is determined finally with concatenating the result of the arithmetic expression with the unmodified post-comma number string.
It is possible to get the Difference value also always with two digits by inserting following additional command lines above echo Difference is: %Difference%:
if %Difference:~0,1% == - (
if %Difference:~2,1% == . set "Difference=-0%Difference:~1%"
) else (
if %Difference:~1,1% == . set "Difference=0%Difference%"
)
This solution avoids also the problem that floating point result of PowerShell is formatted according to region and language settings. For example in Germany and Austria the decimal symbol is , and not . which means the subtraction result output by PowerShell for 15.3 - 12.0 is 3,3 and not 3.3.
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.
echo /?
endlocal /?
findstr /?
for /?
goto /?
if /?
set /?
setlocal /?
See also single line with multiple commands using Windows batch file.
This is not technically an answer, as you've already received and accepted one a perfectly good one.
It is just to allow you to visualise a method of taking the string from your file, splitting it at the decimal point and subtracting 12, from a whole number greater or equal to 12, (see the accepted answer for whole numbers less than 12), all without 'loops' or PowerShell
#Echo Off
Rem Create a variable from the first line of your file
Set /P "newvalue="<"file.tmp"
Echo [%newvalue%]
Rem Exit if the string 'value' does not exist in '%newvalue%'
If "%newvalue%"=="%newvalue:*value=%" Exit /B
Rem ReSet the variable to everything after the string 'value'
Set "newvalue=%newvalue:*value=%"
Echo [%newvalue%]
Rem ReSet the variable to everything up to the first 'space' character
Set "newvalue=%newvalue: ="&:"%"
Echo [%newvalue%]
Rem ReSet the variable, removing the unneeded leading '=' character
Set "newvalue=%newvalue:~1%"
Echo [%newvalue%]
Rem Set a new variable to the whole number, i.e. everything up to the first '.' character
Set "whole=%newvalue:.="&:"%"
Echo [%whole%]
Rem Set a new variable to the decimal, i.e. everything after the '.' character
Set "decimal=%newvalue:*.=%"
Echo [%decimal%]
Rem Subtract 12 from the whole number
Set /A remainder=100+whole-112
Echo [%remainder%]
Rem ReJoin the variables to show the difference
Echo [%remainder%.%decimal%]
Pause
Obviously in your script proper, you'd only need:
#Echo Off
Set /P "newvalue="<"file.tmp"
If "%newvalue%"=="%newvalue:*value=%" Exit /B
Set "newvalue=%newvalue:*value=%"
Set "newvalue=%newvalue: ="&:"%"
Set "newvalue=%newvalue:~1%"
Set "whole=%newvalue:.="&:"%"
Set "decimal=%newvalue:*.=%"
Set /A remainder=100+whole-112
Echo %remainder%.%decimal%
Pause
I have a huge text file which look like this:
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,3
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,8
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,14
36,53,15596,0.58454577855,0.26119,2.24878677855,0.116147072052964,12
The desired output is this:
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-03
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-08
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-14
36,53,15596,0.58454577855,0.26119,2.24878677855,0.116147072052964,MI-12
I have tried other relevant posts here and on other communities but could not exactly get what I want.
UPDATE
This is the cross-question (I wanted both Unix/perl answers and batch/powershell solutions for this.) that has interesting answers.
Here's a PowerShell answer in case you like PS.
Get-Content C:\Path\To\File.csv |
Where{$_ -match '^(.*,)([^,]*)$'} |
ForEach { "{0}MI-{1}" -f $Matches[1], $Matches[2].Padleft(2,'0') } |
Set-Content C:\Path\To\NewFile.csv
The next code does what you want except for filling with zero the last token when is less than 10, hope it helps.
EDIT: I figured out a way to insert a leading zero when the last number is less than 10. A little bit ugly but does it. :)
#echo off
setlocal EnableDelayedExpansion
for /F "delims=, tokens=1-8" %%A in (f.txt) do (
set /a "t=%%H-10"
if "!t:~0,1!" equ "-" (set "n=0%%H") else (set "n=%%H")
echo(%%A,%%B,%%C,%%D,%%E,%%F,%%G,MI-!n!>>f.new.txt
)
move /Y f.new.txt f.txt >nul 2>&1
For file (f.txt in this case):
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,3
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,8
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,14
36,53,15596,0.58454577855,0.26119,2.24878677855,0.116147072052964,12
Produces the following result (also in f.txt): updated
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-03
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-08
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-14
36,53,15596,0.58454577855,0.26119,2.24878677855,0.116147072052964,MI-12
Here is a cmd batch file that relies on a nice hack to split off the last item of a comma-separated list, independent on how many commas occur in the string. The basic technique is shown in the following; note that this requires delayed expansion to be enabled:
set "x=This,is,the,original,list."
set "y=" & set "z=%x:,=" & set "y=!y!,!z!" & set "z=%" & set "y=!y:~1!"
echo ORIGINAL: %x%
echo LAST ITEM: %z%
echo REMAINDER: %y%
So here is the code of the script, holding the above method in a sub-routine called :GET_LAST_ITEM:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILE=%~1" & rem // (specify the CSV file by the first argument)
for /F "usebackq delims=" %%L in ("%_FILE%") do (
call :GET_LAST_ITEM LAST REST "%%L"
setlocal EnableDelayedExpansion
set "LAST=0!LAST!"
echo(!REST!,MI-!LAST:~-2!
endlocal
)
endlocal
exit /B
:GET_LAST_ITEM rtn_last rtn_without_last val_string
::This function splits off the last comma-separated item of a string.
::Note that exclamation marks must not occur within the given string.
::PARAMETERS:
:: rtn_last variable to receive the last item
:: rtn_without_last variable to receive the remaining string
:: val_string original string
setlocal EnableDelayedExpansion
set "STR=,%~3"
set "PRE=" & set "END=%STR:,=" & set "PRE=!PRE!,!END!" & set "END=%"
endlocal & set "%~1=%END%" & set "%~2=%PRE:~2%"
exit /B
This is the answer that #RomanPerekhrest provided at my cross-question (I was also seeking unix/perl solutions) here:
awk approach with sprintf function(to add leading zeros):
awk -F, -v OFS=',' '$8="MI-"sprintf("%02d",$8);' file
The output:
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-03
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-08
36,53,90478,0.58699759849,0.33616,4.83449759849,0.0695335954050315,MI-14
36,53,15596,0.58454577855,0.26119,2.24878677855,0.116147072052964,MI-12
I have a series of files that have long filenames. For each filename that contains a hyphen I would like to keep the substring in position 6-8, append the _FM07_FY14.prn to the name and ignore the rest of the original filename. The new extension is now .prn. The two digits 07 stands for the month and 14 is the year. The month and year can be found from the "date created" property. Will appreciate it if you can show me how to automatically capture this mm and yy from the date created. Hardcoding this part is okay too since I can sort files by created dates and put them in separate folders.
For example
aaaaaD07.dfdd-1234.A.b.1233 new filename will be D07_FM01_FY14.prn
bbcbaA30dls-d343.a.123d new filename will be A30_FM01_FY14.prn
cdq0dG12ir3-438d.dfd.txt new filename will be G12_FM01_FY14.prn
This is the .bat file I come up with after reading many posts on here, and I don't know how to extract the mm and yy so I hard code it. I am not familiar with Powershell. I can only handle a .bat or .cmd file and run it at the command prompt. Any and all help will be highly appreciated. Thanks!
#ECHO OFF
SETLOCAL
for %%F in (*.*) do (
SET "name=%%a"
set "var=_FM01_FY14.prn"
ren *-* "%name:~6,8%var%"
)
*endlocal*
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\one"
PUSHD %sourcedir%
FOR /f "delims=" %%a IN (
'dir /b /a-d "*" '
) DO (
SET name=%%a
SET fdate=%%~ta
ECHO(REN "%%a" "!name:~5,3!_FM!fdate:~3,2!_FY!fdate:~8,2!.prn"
)
popd
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
The format that I use for date is dd/mm/yyyy If yours is different, then you'll need to change the offset in the !fdate:~m,2! phrases. The value of m is the offset into the date string from the first character (the second parameter is the number of characters to select.)
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.
Ok
It's simple. I just want to add 1 to a number every time trought the use of += operator!
So i go in prompt just like this:
C:\Users\fsilveira>SET teste=000007
C:\Users\fsilveira>ECHO %teste%
000007
C:\Users\fsilveira>SET /A teste+=1
8
C:\Users\fsilveira>
Wow nice. Seems to be working just fine.
From the behaviour of the last one, if I use the same operator again, it should just add one to eight right? So I guess I will have 9? But here is what's happening:
C:\Users\fsilveira>SET teste=000008
C:\Users\fsilveira>ECHO %teste%
000008
C:\Users\fsilveira>SET /A teste+=1
1
C:\Users\fsilveira>
What? 8 + 1 is 1 ? o_O
When it comes to the number 8 it does not work how it should (or how I believe it's suppose to)
I'm going insane over here.
Please some one could help me and explain to me what's happening?
I really dont know!
Regards,
Filipe
When prefixing with 0 it is intrepeated as an octal number. And 00008 is not a valid octal number. You can see the effect of this by the following:
C:\Users>SET teste=000020
C:\Users>ECHO %teste%
000020
C:\Users>SET /A teste+=1
17
where 00020 in octal is 16 in decimal.
The number 8 doesn't have 5 leading zeros. If you're doing math, use real numbers. :-)
This works fine on my machine in a command window in Win7 64-bit:
C:\Users\Ken>set /a teste=8
8
C:\Users\Ken>set /a teste+=1
9
C:\Users\Ken>set /a teste+=1
10
C:\Users\Ken>echo %test3%
10
C:\Users\Ken>
You can avoid this by removing leadig zeros:
C:\>set teste=000008
C:\>echo %teste%
000008
C:\>for /f "tokens=1*delims=0" %i in ("$0%teste%") do #set teste=%j
C:\>set /a teste+=1
9
The leading zeros makes things complicated; I would have expected the '000007' not to have worked in that way - the bottom line is: the '000008'; or any '8' with leading zeros is being handled as a string. Example:
C:\Users\op>set f=foo
C:\Users\op>echo %f%
foo
C:\Users\op>set /a f+=1
1
In response to
"number 7 doesn't have 5 leading zeros too.. but still works with the operator!"
You will only have trouble with the leading zeros for numbers > 7 because 0-7 Octal are the same as 0-7 Decimal!