Reputation: 177
I have been using batch for a while but not an expert of it.
I recently wrote a piece of code to do the following thing:
Get a date input from the command line. If its format is like 20200102
, it is converted to 2020-01-02
and get its previous date 2020-01-01
.
So I wrote the following code and save it in test.bat
:
set output_file_date=%~1
set run_date="%output_file_date%"
set output_file_date=%output_file_date_PARAM%
set run_date=%output_file_date:~0,4%-%output_file_date:~4,2%-%output_file_date:~6,2%
FOR /f "usebackq" %%i IN (`PowerShell ^(Get-Date^('%run_date%'^)^).AddDays^(-1^).ToString^('yyyy-MM-dd'^)`) DO SET run_start_date=%%i
ECHO change the date format to "%run_start_date%"
pause
The output on running this batch file from within a command prompt window is:
D:\>test.bat "20250204"
D:\>set output_file_date=20250204
D:\>set run_date="20250204"
D:\>set output_file_date=20250204
D:\>set run_date=2025-02-04
D:\>FOR /F "usebackq" %i IN (`PowerShell (Get-Date('2025-02-04')).AddDays(-1).ToString('yyyy-MM-dd')`) DO SET run_start_date=%i
D:\>SET run_start_date=2025-02-03
D:\>ECHO change the date format to "2025-02-03"
change the date format to "2025-02-03"
D:\>pause
Press any key to continue . . .
As you can see, the code worked as expected.
Then I moved the above code piece to a batch file with a little bit modification. I put it in a procedure.
Some other codes ahead
...
echo "%output_file_date%"
set run_date="%output_file_date%"
set check=%output_file_date:~4,1%
echo "%check%"
IF "%check%" NEQ "-" (
set run_date=%output_file_date:~0,4%-%output_file_date:~4,2%-%output_file_date:~6,2%
echo "%run_date%"
FOR /f "usebackq" %%i IN (`PowerShell ^(Get-Date^('%run_date%'^)^).AddDays^(-1^).ToString^('yyyy-MM-dd'^)`) DO SET run_start_date=%%i
ECHO change the date format to "%run_start_date%"
)
Then the code piece stopped working. The output became as:
D:\>complex.bat "20250203"
"20250203"
""
""
change the date format to ""
I also removed @echo off at the top, the output of this piece of code is as following.
echo "20250203"
set run_date="20250203"
set check=0
echo ""
IF "" NEQ "-" (
set run_date=2025-02-03
echo ""
FOR /F "usebackq" %i IN (`PowerShell (Get-
Date('')).AddDays(-1).ToString('yyyy-MM-dd')`) DO SET
run_start_date=%i
ECHO change the date format to ""
)
Does anyone know what was happening here? It seems the set stopped working? How can I fix this?
Upvotes: -1
Views: 69
Reputation: 38708
As it is so far unmentioned, there is no issue with the SET command behaving differently, because the output from test.bat "20250204"
you submitted is not a true reflection of what happens.
So let's start with the actual output from test.bat
:
D:\>test.bat "20250204"
D:\>set output_file_date=20250204
D:\>set run_date="20250204"
D:\>set output_file_date=
D:\>set run_date=~0,4output_file_date:~4,2output_file_date:~6,2
D:\>FOR /F "usebackq" %i IN (`PowerShell (Get-Date('~0 4output_file_date:~4 2output_file_date:~6 2')).AddDays(-1).ToString('yyyy-MM-dd')`) DO SET run_start_date=%i
D:\>SET run_start_date=Get-Date
D:\>SET run_start_date="System.DateTime".
D:\>SET run_start_date=At
D:\>SET run_start_date=+
D:\>SET run_start_date=+
D:\>SET run_start_date=+
D:\>SET run_start_date=+
D:\>ECHO change the date format to "+"
change the date format to "+"
D:\>pause
Press any key to continue . . .
As you can clearly see, your issue is that there is no variable defined with the name output_file_date_PARAM
, and therefore what you are inevitably passing to PowerShell, as %run_date%
, is not a date string, i.e. ~0,4output_file_date:~4,2output_file_date:~6,2
!
Now let's move onto your second example output, (we have to assume that you once again used set output_file_date=%~1
within Some other codes ahead
):
D:\>complex.bat "20250203"
D:\>echo "20250203"
"20250203"
D:\>set run_date="20250203"
D:\>set check=0
D:\>echo "0"
"0"
D:\>IF "0" NEQ "-" (
set run_date=2025-02-03
echo ""20250203""
FOR /F "usebackq" %i IN (`PowerShell (Get-Date('"20250203"')).AddDays(-1).ToString('yyyy-MM-dd')`) DO SET run_start_date=%i
ECHO change the date format to "+"
)
""20250203""
D:\>SET run_start_date=Get-Date
D:\>SET run_start_date=not
D:\>SET run_start_date=At
D:\>SET run_start_date=+
D:\>SET run_start_date=+
D:\>SET run_start_date=+
D:\>SET run_start_date=+
change the date format to "+"
This clearly does not match what you have shown us as the output from complex.bat "20250203"
, given your complex.bat
content.
In summary, nothing you have submitted reflects a mcve, and does not show a command behaving differently with the SET command.
As a courtesy, here's what your complex.bat
code snippet should have looked like:
Echo "%output_file_date%"
Set "run_date=%output_file_date%"
Set "check=%output_file_date:~4,1%"
Echo "%check%"
If Not "%check%" == "-" (
Set "run_date=%output_file_date:~0,4%-%output_file_date:~4,2%-%output_file_date:~6,2%"
SetLocal EnableDelayedExpansion
Echo "!run_date!"
For /F %%G In ('%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "(Get-Date('!run_date!')).AddDays(-1).ToString('yyyy-MM-dd')"') Do Set "run_start_date=%%G"
Echo change the date format to "!run_start_date!"
EndLocal
)
I have added this because you need to understand that any variable modified within a parenthesized code block requires expanding not at block read time, (the default), but at run time, i.e. delayed.
Expected output, (in comparison to your submission):
D:\>complex.bat "20250203"
"20250203"
"0"
"2025-02-03"
change the date format to "2025-02-02"
Delaying variable expansion is one of the most common problems reported on this site under the batch-file tag, and as such also renders your question an off topic duplicate.
Upvotes: 0
Reputation: 80173
When a batch file starts, its errorstate
is ON. This means that any command read from the batch file is first displayed (echo
ed) and then executed.
Consequently, if the first command is echo off
then that line is displayed, then executed - so echo off
would be displayed and the remainder of the commands NOT displayed before execution as the echostate
has now been turned OFF.
Any command preceded by @
will NOT be displayed before execution, so @echo off
as the first line of a batch will NOT display the command , then turn echostate
to OFF.
setlocal
causes a new subshell
to be entered. The parameters applied to the setlocal
command sets the delayedexansion
and extensions
mode of the subshell and the subshell acquires its own copy of the then-current environment.
When an endlocal
command is reached, then the new environment is discarded and the original environment is restored, hence variables created, deleted or altered in the subshell will disappear, reappear or revert to their original value.
A quirk of how the language is parsed means that the command endlocal&set "varname=%varname%"
will allow a new variable value to be exported
from the setlocal/endlocal
"bracket".
&
simply separates commands that will be executed consecutively.
I always use
@echo off
setlocal
since I often turn echo
on for debugging. It's easier for me to move to the top of the file, then the end of the line to alter off
to on
or vice-versa, instead of having to move the cursor over either the @echo o
or the &setlocal…
Upvotes: 2