批处理脚本:验证日期输入

3

我有一个批处理文件,用于为客户创建新的项目文件夹,并将适当的文件和文件夹添加到中心位置,以便用户可以按照创建过程进行操作。我需要添加一个输入部分,以便他们可以输入日期(不一定是当前日期),并将其包含在文件命名中。

我的问题是,我需要对日期输入进行校验。我希望用户使用 MM-DD-YYYY 格式来输入日期,包括短横线。然后它需要将其格式化为 YYYY-MM-DD 格式。它必须足够智能,强制用户使用所需的 MM-DD-YYYY 格式;只能是数字和短横线,不能有斜杠等其他字符。

我一直在寻找答案,但却找不到我需要的东西,因此我请求各位天才的帮助,因为这让我感到非常沮丧。以下是我的脚本代码。我需要在选择工作类型后立即进行此输入:"Please insert date (MM-DD-YYYY format): "

@echo off
setlocal EnableDelayedExpansion
set version=7.95
set projectpath="P:"
set workbookpath="\\server2\Documents\Blanks (DO NOT EDIT)\dryingworkbook_v3r75.xls"
set questions="\\server2\Documents\Blanks (DO NOT EDIT)\Abatement and Mold Questions.txt"
set notes="\\server2\Documents\Blanks (DO NOT EDIT)\Job Notes.docx"
set info="\\server2\Documents\Blanks (DO NOT EDIT)\Job Information.docx"
set bizname=1

ECHO =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ECHO =  Welcome to SERVPRO Project Creation Wizard v%version%  =
ECHO =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ECHO.

:sof
ECHO.
ECHO Is this new project for a Residential or Commercial job?
:loopJobType
SET /P jobtype=Enter [r] for Residential or [c] for Commercial:  
ECHO.
IF "%jobtype%" == "r" GOTO :loopResidential
IF "%jobtype%" == "R" GOTO :loopResidential
IF "%jobtype%" == "c" GOTO :loopCommercial
IF "%jobtype%" == "C" GOTO :loopCommercial
GOTO :loopJobType

:loopResidential
ECHO You have chosen to create a new Residential job project.
ECHO.
set type=1
GOTO :loopFirstName

:loopCommercial
ECHO You have chosen to create a new Commercial job project.
ECHO.
set type=2
SET /p bizname=Please enter the business name:  
ECHO.
IF "%bizname%"=="" GOTO :loopCommercial

:loopFirstName
SET /P FirstName=Please enter the insured's first name:  
IF "%FirstName%"=="" GOTO :loopFirstName
call :format FirstName

:loopLastName
ECHO.
SET /P LastName= Please enter the insured's last name:  
IF "%LastName%"=="" GOTO :loopLastName
call :format LastName
SET FullName=%LastName%, %FirstName%
SET FullBizName=%bizname% (%FullName%)
goto :ConfirmProject

:format
set Name=!%1!
set Head=%Name:~0,1%
set Tail=%Name:~1%
for %%a in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do set Head=!Head:%%a=%%a!
for %%a in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do set Tail=!Tail:%%a=%%a!
set %1=%Head%%Tail%
GOTO :eof

:ConfirmProject
ECHO.
IF "%type%" == "1" SET /P yesno=Are you sure you want to add "%FullName%" to the Project directory? [y/n]  
IF "%type%" == "2" SET /P yesno=Are you sure you want to add "%FullBizName%" to the Project directory? [y/n]  
IF "%yesno%" == "y" GOTO :CreateProject
IF "%yesno%" == "Y" GOTO :CreateProject
IF "%yesno%" == "n" GOTO :sof
IF "%yesno%" == "N" GOTO :sof
GOTO :ConfirmProject

:CreateProject
IF "%type%" == "1" SET ProjectName=%FullName%
IF "%type%" == "2" SET ProjectName=%FullBizName%

:: Create a folder containing a new project.
mkdir "%projectpath%\%ProjectName%"
ECHO.
ECHO.
ECHO Creating a Project directory for "%ProjectName%" ...

:: Create a folder within said project that will contain job documents.
ECHO Creating a Documents directory for "%ProjectName%" ...
mkdir "%projectpath%\%ProjectName%\Documents"
:: (Taken out of use 7-15-13) ECHO Adding a Job Information file for "%ProjectName%" ...
:: (Taken out of use 7-15-13) copy /-Y %info% "%projectpath%\%ProjectName%\Documents\Job Information - %ProjectName%.docx"
ECHO Documents directory creation for "%ProjectName%" finished ...

:: Create a folder within said project that will contain drying workbook(s).
ECHO Creating a Drying Workbook directory for "%ProjectName%" ...
mkdir "%projectpath%\%ProjectName%\Drying Workbooks"

:: Copy a new blank workbook to the project workbook directory and give it the proper name.
ECHO Adding a Drying Workbook for "%ProjectName%" ...
copy /-Y %workbookpath% "%projectpath%\%ProjectName%\Drying Workbooks\DRY 1_%ProjectName%.xls"
ECHO Adding an Abatement and Mold Questions file for "%ProjectName%" ...
copy /-Y %questions% "%projectpath%\%ProjectName%\Drying Workbooks\Abatement and Mold Questions.txt"
ECHO Drying Workbook directory creation for "%ProjectName%" finished ...

:: Create a folder within said project that will contain original photos.
ECHO Creating a Photos directory for "%ProjectName%" ...
mkdir "%projectpath%\%ProjectName%\"Photos

:: Create a folder within said project photo folder that will contain resized photos.
mkdir "%projectpath%\%ProjectName%\Photos\Resized"
mkdir "%projectpath%\%ProjectName%\Photos\Upload"
ECHO Photos directory creation for "%ProjectName%" finished ...

:: Add in Job Notes file.
ECHO Adding a Job Notes files for "%ProjectName%" ...
copy /-Y %notes% "%projectpath%\%ProjectName%\Job Notes - %ProjectName%.docx"

::  Log the creation of the project.
FOR /F "TOKENS=1* DELIMS= " %%A IN ('DATE/T') DO SET CDATE=%%B
For /f "tokens=2-4 delims=/ " %%a in ('date /t') do (set date=%%a%%b%%c)
echo off >  "%projectpath%\Logs\%ProjectName% - [Project Created %date% by %computername%].txt"
ECHO Logging "%ProjectName%" creation date and time...
ECHO Project directory creation for "%ProjectName%" finished ...
GOTO :OpenProject

:OpenProject
:: Ask if the project should be opened now.  If so open and close script, else close script.
set /p reply=Do you want to open the "%ProjectName%" project now? [y/n]
if "%reply%" == "y" %SystemRoot%\explorer.exe "%projectpath%\%ProjectName%"
IF "%yesno%" == "Y" %SystemRoot%\explorer.exe "%projectpath%\%ProjectName%"
GOTO :eof
IF "%yesno%" == "n" GOTO :No
IF "%yesno%" == "N" GOTO :No
exit

:No
ECHO.
ECHO.
ECHO You have successfully created a new project for %ProjectName%.
ECHO.
ECHO Press any key to exit . . .
PAUSE>NUL

:eof
6个回答

5
下面的批处理文件检查插入的日期是否具有正确的格式和表示有效日期,即每个月都有正确的天数,即使是闰年的二月份!
@echo off
setlocal EnableDelayedExpansion

set i=0
for %%a in (31 28 31 30 31 30 31 31 30 31 30 31) do (
   set /A i+=1
   set dpm[!i!]=%%a
)

set /P "inDate=Please insert date (MM-DD-YYYY format): "
if "%inDate:~2,1%%inDate:~5,1%" neq "--" goto invalidDate
for /F "tokens=1-3 delims=-" %%a in ("%inDate%") do set "MM=%%a" & set "DD=%%b" & set "YYYY=%%c"
ver > NUL
set /A month=1%MM%-100, day=1%DD%-100, year=1%YYYY%-10000, leap=year%%4  2>NUL
if errorlevel 1 goto invalidDate
if not defined dpm[%month%] goto invalidDate
if %leap% equ 0 set dpm[2]=29
if %day% gtr !dpm[%month%]! goto invalidDate
if %day% lss 1 goto invalidDate
echo Date correct: %YYYY%-%MM%-%DD%
goto :EOF

:invalidDate
echo Bad date

@MattWilliamson:set /A 命令在出现错误时将 errorlevel 设置为非零值,否则它不会改变 errorlevel。ver 命令在 set /A 之前将 errorlevel 设置为零,如果代码确保此时 errorlevel 为零,则可以将其删除。 - Aacini
啊,那很聪明 ;) - Matt Williamson
为什么 Stack Overflow(SO)没有为批处理脚本提供彩色语法? - Arian Faurtosh

4
您可以使用findstr命令轻松检查字符串是否有效。
set /p date= Please insert date (MM-DD-YYYY format):
echo %date%| findstr /r "^[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]$">nul
if errorlevel 1 (
    echo invalid date
)
pause

(^表示行首,$表示行尾。)

现在要将MM-DD-YYYY格式重新排列为YYYY-MM-DD格式,您可以将字符串拆分并重新组合。由于这是一个固定的格式,这也不太难:

set yyyy=%date:~6,4%
set mm=%date:~0,2%
set dd=%date:~3,2%
set newDate=%yyyy%-%mm%-%dd%
echo %newDate%

每个命令中的第一个数字表示字符串被截取的起始位置,第二个数字表示所截取子字符串的长度。

这个代码怎样验证月份是有效的数字(1-12),并且所选日期也是该月份的有效日期? - Squashman
这个人问道:“我想让用户以MM-DD-YYYY的格式输入日期,包括破折号。” 我尽力提供了强制执行此格式的代码,同时保持代码简单易读。 如果必须是有效日期,则显然需要额外的逻辑。但是,这个解决方案似乎干净而且非常适用于其他想要格式化输入的人。 - Wouter

1
我写了一个名为:getdate的函数,用于测试日期,请试用; 它会测试分隔符是否正确,月份和日期的值范围以及 这些值是否为数字。
@ECHO OFF
setlocal enabledelayedexpansion

:GetDate
set /p $D=Enter a date (MM-DD-YYYY) :

set $separate=%$d:~2,1% %$d:~5,1%

for %%a in (%$separate%) do (if "%%a" neq "-" (echo Wrong Separator : %%a
                                               pause
                                               goto:Getdate))


set $D=%$D:-= %
set $c=1

for %%a in (%$d%) do (call:test !$c! %%a
                      set /a $c+=1)

if !$c!==4 set $DateOK=%$month%-%$day%-%$Year%
echo This DATE IS OK %$dateOK%
exit /b

:test
if %1 equ 1 (echo %2 | findstr [0-9][0-9]
             if errorlevel 1 (echo Unvalid value for Month [NOT NUM]: %2
                              pause
                              goto:getdate)

             if %2 GTR 12 (echo Unvalid value for Month [VALUR RANGE +]: %2
                                pause
                                goto:getdate)
              if %2 LSS 1 (echo Unvalid value for Month [VALUR RANGE -]: %2
                                pause
                                goto:getdate)
              set $month=%2) 



if %1==2  (echo %2 | findstr [0-9][0-9]
           if errorlevel 1 (echo Unvalid value for Day [NOT NUM]: %2
                            pause
                            goto:getdate)
           if %2 GTR 31 (echo Unvalid value for Day [VALUR RANGE +] : %2
                              pause
                              goto:getdate)
           if %2 LSS 01 (echo Unvalid value for Day [VALUE RANGE -]: %2
                              pause
                              goto:getdate)
           set $day=%2)


if %1==3  (echo %2 | findstr [0-9][0-9][0-9][0-9]
           if errorlevel 1 (echo Unvalid value for Year [NOT NUM] : %2
                            pause
                            goto:getdate)
         set $Year=%2)

1
@ECHO OFF
SETLOCAL enabledelayedexpansion
CALL :getverdate
ECHO DATE %indate% is OK.
GOTO :EOF
::
:: Get and verify date in format mm-dd-yyyy; reformat as yyyy-mmm-dd
::
:regetdate
ECHO "%indate%" is not in format "MM-DD-YYYY" or is invalid
:getverdate
SET /p indate="Please insert date (MM-DD-YYYY format): "
IF NOT "%indate:~2,1%%indate:~5,1%"=="--" GOTO regetdate
SET checkdate=9%indate:-=%
IF NOT "%checkdate:~8%"=="%checkdate:~8,1%" GOTO regetdate
FOR %%a IN (0 1 2 3 4 5 6 7 8 9) DO SET checkdate=!checkdate:%%a=!
IF DEFINED checkdate GOTO regetdate
IF %indate:~3,2%==00 GOTO regetdate
FOR %%i IN (01:31 02:29 03:31 04:30 05:31 06:30 07:31 08:31 09:30 10:31 11:30 12:31) DO (
 FOR /f "tokens=1,2delims=:" %%j IN ("%%i") DO IF %%j==%indate:~0,2% if "%%k" geq "%indate:~3,2%" GOTO goodday 
)
GOTO regetdate
:goodday
IF "%indate:~-4%" geq "1980" IF "%indate:~-4%" leq "2099" GOTO goodyear
GOTO regetdate
:goodyear
SET /a checkdate=%indate:~-4% %% 4
IF "%indate:~0,2%%indate:~3,2%"=="0229" IF %checkdate% neq 0 GOTO regetdate
SET indate=%indate:~-4%-%indate:~0,2%-%indate:~3,2%
GOTO :eof

这是另一个“获取和验证日期”的例程。
请注意,在您的代码中,您永远不应设置名为date的变量。%date%将返回当前日期 - 这是CMD控制的“魔法变量”。其他此类变量包括%time%%random%%errorlevel%。设置任何一个这样的变量都会覆盖系统建立的值。

0
您可以向用户提供三个提示 - 年份、月份、日期。
set /p y="Please enter year (YYYY): "
set /p m="Please enter month (MM): "
set /p d="Please enter day (DD): "
set date=%y%-%m%-%d%

如果您想要验证输入的长度,可以使用以下代码:

if [%y:~4%] NEQ [] echo year entered incorrectly & goto :getDate

假定如果%y%大于四个字符 - 即如果%y:~4%不为null,则可以假定其已被错误输入(请参见Dos Tips on string manipulation)。相同的原则适用于日和月,只不过它们应该是两个字符。
显然,在用户输入之前,您需要添加标签:getDate


如果人们输入abcd,则会显示该年份有效。 - foxidrive
不要废话,他显然正在寻找一个相当简单的解决方案。 - unclemeat

0

您可以使用ReadFormattedLine子程序进行各种格式化输入。例如,下面的命令以日期格式读取3个数字;该例程只接受数字,插入连字符并在读取最后一个数字后自动继续。如果用户删除字符,则连字符也会自动删除。

call :ReadFormattedLine myDate="##-##-####" /M "Please insert date (MM-DD-YYYY format): "

这个子程序是用纯批处理编写的,因此不需要任何额外的程序,并且它允许进行多个格式化输入操作,例如读取密码、将字母转换为大写等。您可以从按特定格式读取一行下载ReadFormattedLine子程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接