如何在批处理文件中的同一行上设置多个因变量?

时间:2018-01-07 18:31:13

标签: windows batch-file cmd command-prompt

这受another post on Arqade启发,关于如何使用批处理脚本备份某些文件。

以下块使用当前日期/时间字符串创建变量:

REM -- Get the current date/time stamp
set DS=%date%
set TS=%time: =0%
set mm=%DS:~4,2%
set dd=%DS:~7,2%
set yyyy=%DS:~10,4%
set hh=%TS:~0,2%
set min=%TS:~3,2%
set ss=%TS:~6,2%
set ms=%TS:~9,2%
set DT_STAMP=%yyyy%-%mm%-%dd%_%hh%.%min%.%ss%

作为脚本编写者,将这些内容合并为一行通常很方便。然而,在这种情况下,如果不是不可能的话,将其冷凝成单线是非常困难的。

似乎没有办法将多个set命令放在一行上。使用&&&分隔命令可以正常工作,但右侧的命令不能依赖于先前在同一行上设置的变量。

另外,请注意time变量必须如何将spaces替换为零0。似乎没有办法让字符串替换在同一行上获取子字符串。

有什么方法可以将它降到一行吗?我能得到的最接近的是两行:

set DS=%date% && set TS=%time: =0%
set DT_STAMP=%DS:~10,4%-%DS:~4,2%-%DS:~7,2%_%TS:~0,2%.%TS:~3,2%.%TS:~6,2%

更新

这已经收集了一些好的答案;请允许我澄清接受的答案必须具备的内容:

  • 一个丑陋但可重复使用的单行解决方案可以 (上面已经提供了漂亮的版本)
  • 仅限标准shell命令 (必须避免使用PowerShell和类似功能;如果我需要,请在PowerShell中完成所有操作)
  • 能够以任何逻辑格式格式化日期(应该可以执行以上漂亮版本可以执行的任何操作,而不仅仅是使用简写符号支持ISO格式的日期)
  • 可以忽略本地化设置(真的,这一次!)
  • 必须在一行上! (例如,delayed expansion很方便,但可能会产生令人讨厌的副作用,并不能在每种情况下重复使用,并且通常需要多行)

4 个答案:

答案 0 :(得分:3)

使用FOR / F语句很简单,但是正如npocmaka已经说过的那样,它取决于本地化。

我认为您的日期/时间看起来像

Fri 09/14/2018
 8:15:46.12

那么这行得通

for /F "tokens=1-7 delims=/:,. " %%1 in ("%date% %time: =0%") do set DT_STAMP=%%4-%%2-%%3_%%5.%%6.%%7

但是对于德国本地化,您需要

for /F "tokens=1-7 delims=/:,. " %%1 in ("%date% %time: =0%") do set DT_STAMP=%%3-%%2-%%1_%%4h%%5m%%6s

带有批处理宏的解决方案

您可以构建一个批处理宏,使用宏更具可读性,但是宏本身要复杂一些

使用宏,它将当前时间戳分配给变量myDT1:

%@AssignTimeStamp% myDT1
echo %myDT1%

宏的定义(在定义宏时必须禁用延迟扩展):

REM *** Macro definition, be sure that there aren't any trailing whitespaces
set ^"LF=^
%= This creates a variable containing a single linefeed (0x0A) character =%
^"
:: Define %\M% to effectively issue a newline with line continuation
set ^"\M=^^^%LF%%LF%^%LF%%LF%^^"

set @AssignTimeStamp=for %%. in (1 2) do if %%.==2 (%\M%
    %= The next line builds the timestamp =%%\M%
    for /F "tokens=1-7 delims=/:,. " %%1 in ("%date% %time: =0%") do set timestamp=%%4-%%2-%%3_%%5h%%6m%%7s%\M%
    %= When a variable name was give then store the timestamp there, else output the timestamp =%%\M%
    for /F "tokens=1,2 delims=, " %%1 in ("#,!argv!") do ( %\M%
        for %%V in (!timestamp!) do endlocal^&if "%%~2" neq "" (set "%%2=%%V") else echo ##%%V%\M%
    )%\M%
) else setlocal enableDelayedExpansion^&set argv=,

答案 1 :(得分:2)

是的,你可以 - delayed expansion

setlocal enableDelayedExpansion&set "DS=%date%"&& set "TS=%time: =0%"&set "DT_STAMP=!DS:~10,4!-!DS:~4,2!-!DS:~7,2!_!TS:~0,2!.!TS:~3,2!.!TS:~6,2!"

但这是格式化日期时间的一种不好的方式,因为它取决于本地化设置。为什么要在一行上执行此操作?对于与设置无关的方法检查this

答案 2 :(得分:2)

您似乎想要一个类似ISO-8601的格式化日期。无论系统的区域/文化设置如何,这都将生成格式正确的日期。如果在.bat脚本文件中使用它,请加倍%变量上的d个字符。

FOR /F %d IN ('powershell -NoLogo -NoProfile -Command Get-Date -UFormat %Y-%m-%d_%H.%M.%S') DO (SET "DT_STAMP=%d")

或者,直接在PowerShell中使用:

$DT_STAMP = Get-Date -UFormat %Y-%m-%d_%H.%M.%S

答案 3 :(得分:2)

可以使用命令行中的日期和时间为region/country dependent的动态环境变量DATETIME根据要求完成此操作:

@set "DT_STAMP=%DATE:~10,4%-%DATE:~4,2%-%DATE:~7,2%_%TIME:~0,2%.%TIME:~3,2%.%TIME:~6,2%" & call set "DT_STAMP=%%DT_STAMP: =0%%"

此命令行期望DATE具有日期字符串,例如Sat 09/15/2018,而TIME具有时间字符串,例如 4:18:23,56,并且采用24小时格式。 / p>

对于给定的示例日期和时间字符串,环境变量DT_STAMP的值为2018-09-15_04.18.23

最好使用命令行:

@set "DT_STAMP=%DATE:~-4%-%DATE:~-10,2%-%DATE:~-7,2%_%TIME:~0,2%.%TIME:~3,2%.%TIME:~6,2%" & call set "DT_STAMP=%%DT_STAMP: =0%%"

第二个版本的优点是日期字符串是否以工作日开头并不重要。但是日期字符串必须具有MM/DD/YYYY(MDY)格式,并带有/.-之类的任何分隔符。

但是大多数国家/地区使用DD.MM.YYY(DMY)(具有不同的分隔符),这需要进行一些小的修改:

@set "DT_STAMP=%DATE:~-4%-%DATE:~-7,2%-%DATE:~-10,2%_%TIME:~0,2%.%TIME:~3,2%.%TIME:~6,2%" & call set "DT_STAMP=%%DT_STAMP: =0%%"

另请参阅What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean?

使用第二个 SET 命令在同一命令行上用0替换所有空格,该命令引用了环境变量DT_STAMP%%而不是%

在执行第一个 SET 命令之前,在将命令行解析为%%的过程中,两个cmd.exe%替换。因此,最终执行的命令行例如:

@set "DT_STAMP=2018-09-15_ 4.18.23" & call set "DT_STAMP=%DT_STAMP: =0%"

使用 CALL 会导致&之后对运算符cmd.exe之后的命令行部分进行第二次解析。因此,第二个 SET 具有参数"DT_STAMP=2018-09-15_04.18.23"作为参数,并且最终为环境变量DT_STAMP分配了所需的日期/时间字符串。

但是更好的方法是以所需格式获取当前本地日期/时间字符串,而与区域/国家/地区设置无关,这也可以通过单个命令行进行:

@for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do @set "DT_STAMP=%%I" & call set "DT_STAMP=%%DT_STAMP:~0,4%%-%%DT_STAMP:~4,2%%-%%DT_STAMP:~6,2%%_%%DT_STAMP:~8,2%%.%%DT_STAMP:~10,2%%.%%DT_STAMP:~12,2%%"

它使用 WMIC 来获取当前本地日期/时间区域的独立性,并再次使用 CALL SET 重新格式化日期/时间字符串包含所需格式的空格。

有关此命令行如何处理 WMIC 输出的说明,请参见我在%date% produces different result in batch file when run from Scheduled Tasks in Server 2016上的回答。

顺便说一句:.通常用作文件名和文件扩展名之间的分隔符。因此,最好在小时和分钟之间以及分钟和秒之间使用-而不是.

另一个注意事项:call之后的命令也可以是任何其他直接使用日期/时间字符串的命令,例如,在文件/文件夹名称/路径中使用的命令。