【问题标题】:Increment folders names numbering using windows command line使用 Windows 命令行增加文件夹名称编号
【发布时间】:2017-11-20 02:01:33
【问题描述】:

每周我都会创建一个新文件夹,并且为了保持它们的顺序,我用 01 - day-month02 - day-month 等编号命名它们。我已经创建了 windows cmd 脚本,根据日期,但现在我想编写将文件夹编号放在日期之前的脚本部分(保持数字用 0 划线)。

我已经在 stackexchange 上看到了一个示例,但无法完全理解代码。

这是我目前得到的:

@echo off
setlocal enableDelayedExpansion
set "baseName= - "
set "n=0"
for /f "delims=" %%F in (
  '2^>nul dir /b /ad "%baseName%*."^|findstr /xri "[0-9]*%baseName%*"'
) do (
  set "name=%%F"
  set "name=!name:*%baseName%=!"
  if !name! gtr !n! set "n=!name!"
)
set /a n+=1
md "%n%%baseName%"

PowerShell 2.0 也是受欢迎的。

【问题讨论】:

  • 如果您的语言环境周定义(一周中的第一天/一年中的第一周)与 ISO8601 匹配,您可以使用来自 Ritchie Lawrence batch function libraryDateToWeek 子/函数
  • 在上述情况下,dir /b /ad "%baseName%*." 列出了当前目录中名称以 开头并以 。我建议你改用DIR/B/AD-S-L "*%baseName%*

标签: batch-file cmd powershell-2.0


【解决方案1】:
@echo off
setlocal enableDelayedExpansion
set "baseName= - "
set "n=0"
for /f "delims= " %%F in (
  '2^>nul dir /b /ad "*%baseName%*."^|findstr /xri /c:"[0-9][0-9]%baseName%.*"'
) do (
  if %%F gtr !n! set "n=%%F"
)
IF %n%==0 (SET /a n=100) ELSE (SET "n=1%n%")
set /a n+=1
echo md "%n:~-2%%baseName%"

GOTO :EOF

相当多的变化,我会解释。

首先,dir 命令以/ad 目录名的/b(基本)形式输出目录列表。正如@compo 所说,您需要*%basename%*,因为您的目录名称将采用nn - something 的形式,而不是以%basename% 开头。 2^>nul 指示来自dir(例如no files found)的任何错误报告都将被禁止。

这个命令的输出然后管道findstr,它选择/xriex实际上,case-I的行敏感地匹配提供的R正则表达式。

原来的regex, "[0-9]%baseName%" 不够用。它试图说出“任意数量的前导数字(包括根本没有前导数字)”,然后是 basename 中的字符串,然后是 *,这意味着“basename 中最后一个字符的任意数量”

此外,由于basename 包含空格,findstr 将每个空格分隔的子字符串视为要查找的单独字符串,因此会尝试查找并精确匹配“任意数量的数字”或“-”或“*”,所以没有结果也就不足为奇了。

替换 regex 首先是 /c:,这意味着“空格是普通字符”,然后正则表达式本身指定两个前导数字,basename 字符串和 .* - 任何数量的任何其他字符(因为您要检查的目录名称将重命名为02 - day-month)。

通过将delims 设置为空格,分配给%%F 的(隐式默认单个令牌)将是列表中目录名中的任何字符(findstr 过滤后),但不包括第一个 空格

所以,现在剩下要做的就是在%%F 中找到最大的数值。这是通过将%%Fn 的值进行比较(以后请使用有意义的名称)并将n 设置为%%F 的值 来完成的。 p>

!n! 语法是必需的,因为 delayed expansion 特性(有很多很多关于此的 SO 文章 - 使用顶部栏中的 search 查找一些)。本质上,在代码块(带括号的一系列命令)中,任何%var% 都会被遇到块时的变量值替换,!var!(调用delayedexpansion)是运行时值(通过块的操作而改变的值)

一旦循环完成,我们会遇到以下两种情况之一:要么没有匹配项(因此n 将具有值0),要么存在匹配项因此n 将具有nn 值,这将是 2 位数字)

set /a 增加n 中的值,这一切都很好 - 除了如果n 恰好是0809,操作将失败,因为cmd 将一个以 0 开头的数字字符串视为 八进制,而 89 不是有效的八进制字符。

解决此问题的标准解决方法是在两位值之前加上一个1。显然,如果n0,然后将1 添加到它会得到1 - 你需要一个前导零,所以在这种情况下,将n 设置为100。 (我使用了强制算术set

增加数字后,md 现在需要n 的最后两个字符,这就是%n:~-2% 的含义。我刚刚echoed 提议的新名称,以防万一出现问题。在测试激活md 后,只需删除echo 关键字即可。

最好剪切和粘贴解决方案,因为批处理可能会表现出奇怪的语法敏感性。它还避免了打字错误。

【讨论】:

  • 我喜欢 IF %n%==0 (SET /a n=100) ELSE (SET "n=1%n%") 背后的想法,以避免对数字进行八进制解释,尽管在 FOR 循环之后使用 set "n=00" 和仅使用 set "n=1%n%" 会更好.而 DIR 选项 /b 表示 bare 而不是 basic 100% 精确。所以我为这个非常好的解释答案 +1。
【解决方案2】:

这是我的解决方案,它使用了一种不同的方法,除了执行 WMIC 命令之外,当当前日期和月份应从本地日期/时间确定时无法避免一种区域和语言独立的方式。重新格式化环境变量 DATE 的字符串会更快,但此日期字符串的格式取决于为使用的用户帐户定义的 Windows 区域设置。

带有大量注释行的批处理代码(以命令REM开头的行):

@echo off
goto GetFolderList

rem Run DIR to get a list of only folder names because of /AD and /B
rem matching the wildcard pattern ?? - ??-?? ordered reverse by name
rem because of /O-N with suppressing the error message DIR outputs if
rem no such folder can be found in current directory by redirecting
rem the error message to device NUL.

rem From folder name just the first two characters are assigned to loop
rem variable I as the default delimiters space/tab are used on splitting
rem up the folder name into tokens. Only the first token is processed by
rem outer FOR with the default options, i.e. the number to increment.

rem The inner FOR executes the SET command only if the first space/tab
rem delimited string of current folder name from DIR output contains any
rem non digit character. So if environment variable Number is defined
rem after inner FOR loop execution, the folder name is not valid as it
rem does not start with a number with 2 digits.

rem On current folder name not starting with a number, the outer FOR loop
rem is processed further with next folder name. Otherwise the number of
rem the current folder name being automatically the highest number because
rem of using leading zeros and reverse output by name is assigned to the
rem environment variable Number and outer FOR loop is exited with a jump
rem to label TrimNumber.

rem The numbering for first folder starts with 01 in case of no (valid)
rem folder matching wildcard pattern ?? - ??-?? is found in current folder.

:GetFolderList
set "Number="
for /F %%I in ('dir /AD /B /O-N "?? - ??-??" 2^>nul') do (
    for /F "delims=0123456789" %%J in ("%%I") do set "Number=none"
    if defined Number (
        set "Number="
    ) else (
        set Number=%%I
        goto TrimNumber
    )
)
set "Number=01"
goto CreateFolder


rem On incrementing a number with leading zeros using an arithmetic expression
rem it can happen easily that the number is interpreted octal, especially if
rem the number would have 3 digits which is not the case here. So it is better
rem to remove the leading zero(s) before incrementing the number by 1.

rem Of course with number from folder name being 00 it happens on removing
rem in a loop the first character from string when being 0 that the number
rem string is suddenly not defined anymore which must result also in an
rem exit of this loop trimming leading zeros.

:TrimNumber
if not "%Number:~0,1%" == "0" goto IncrementNumber
set "Number=%Number:~1%"
if defined Number goto TrimNumber
set "Number=01"
goto CreateFolder


rem After leading zeros are removed it is safe to increment the number by 1.
rem Then it is necessary to make sure that the number has always two digits
rem even on being 1 to 9. This can be achieved by inserting a leading 0 on
rem number string and then get just the last 2 characters from number string.

:IncrementNumber
set /A Number+=1
set "Number=0%Number%"
set "Number=%Number:~-2%"
goto CreateFolder


rem The WMIC command below with used options outputs among blank
rem lines in UTF-16 Little Endian encoding also a line with

rem LocalDateTime=20170617192138.218000+120

rem Of interest from this line is the string after the equal sign as it
rem contains year, month, day, hour, minute, second, microsecond and UTC
rem offset of configured time zone in fixed and region independent format.

rem From this date/time string just the month and day is needed for the
rem folder name in format DD-MM with incremented number being prepended.

:CreateFolder
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "LocalDateTime=%%I"
set "FolderName=%Number% - %LocalDateTime:~6,2%-%LocalDateTime:~4,2%"
md "%FolderName%"

编辑: 正如Magoo 所解释的那样,通过在文件夹名称中的数字字符串前面加上1 来避免使用前导0 对数字进行八进制解释的方法非常棒,并且使代码更加简单并且更快一点:

@echo off
goto GetFolderList

rem Run DIR to get a list of only folder names because of /AD and /B
rem matching the wildcard pattern ?? - ??-?? ordered reverse by name
rem because of /O-N with suppressing the error message DIR outputs if
rem no such folder can be found in current directory by redirecting
rem the error message to device NUL.

rem From folder name just the first two characters are assigned to loop
rem variable I as the default delimiters space/tab are used on splitting
rem up the folder name into tokens. Only the first token is processed by
rem outer FOR with the default options, i.e. the number to increment.

rem The inner FOR executes the SET command only if the first space/tab
rem delimited string of current folder name from DIR output contains any
rem non digit character. So if environment variable Number is defined
rem after inner FOR loop execution, the folder name is not valid as it
rem does not start with a number with 2 digits.

rem On current folder name not starting with a number, the outer FOR loop
rem is processed further with next folder name. Otherwise the number of
rem the current folder name being automatically the highest number because
rem of using leading zeros and reverse output by name is assigned to the
rem environment variable Number and outer FOR loop is exited with a jump
rem to label IncrementNumber.

rem The numbering for first folder starts with 01 in case of no (valid)
rem folder matching wildcard pattern ?? - ??-?? is found in current folder.

:GetFolderList
set "Number="
for /F %%I in ('dir /AD /B /O-N "?? - ??-??" 2^>nul') do (
    for /F "delims=0123456789" %%J in ("%%I") do set "Number=none"
    if defined Number (
        set "Number="
    ) else (
        set Number=%%I
        goto IncrementNumber
    )
)
set "Number=01"
goto CreateFolder


rem On incrementing a number with leading zeros using an arithmetic expression
rem it can happen easily that the number is interpreted octal, especially if
rem the number would have 3 digits which is not the case here. That can be
rem avoided here by changing the strings "00" to "99" to "100" to "199".

rem Next it is safe to increment the number by 1. Then it is necessary to get
rem from number in range 101 to 200 the last 2 characters from number string.

:IncrementNumber
set "Number=1%Number%"
set /A Number+=1
set "Number=%Number:~-2%"
goto CreateFolder


rem The WMIC command below with used options outputs among blank
rem lines in UTF-16 Little Endian encoding also a line with

rem LocalDateTime=20170617192138.218000+120

rem Of interest from this line is the string after the equal sign as it
rem contains year, month, day, hour, minute, second, microsecond and UTC
rem offset of configured time zone in fixed and region independent format.

rem From this date/time string just the month and day is needed for the
rem folder name in format DD-MM with incremented number being prepended.

:CreateFolder
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "LocalDateTime=%%I"
set "FolderName=%Number% - %LocalDateTime:~6,2%-%LocalDateTime:~4,2%"
md "%FolderName%"

还有一点说明:

类似的命令行

set "FolderName=%LocalDateTime:~0,4%-%LocalDateTime:~4,2%-%LocalDateTime:~6,2%

FOR 命令行之后使用 WMIC 命令将定义格式为 YYYY-MM-DD 的文件夹名称,该格式最常用于文件夹和文件名,因为使用此国际日期format 按名称排序的文件和文件夹也会自动按日期排序。

所以当不需要在同一天创建多个文件夹,而文件夹名称中确实需要一个递增的数字时,最好在文件夹名称中使用国际日期格式,并且可以将批处理代码简化为:

@echo off
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "LocalDateTime=%%I"
md "%LocalDateTime:~0,4%-%LocalDateTime:~4,2%-%LocalDateTime:~6,2%

要了解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。

  • dir /?
  • echo /?
  • for /?
  • goto /?
  • if /?
  • rem /?
  • set /?
  • wmic /?
  • wmic os /?
  • wmic os get /?
  • wmic os get localdatetime /?

另请阅读有关Using Command Redirection Operators 的Microsoft 文章,了解2>nul 的解释。重定向运算符> 必须在此处使用插入符号^ 进行转义,以便Windows 命令解释器在解析整个FOR 命令行以及稍后执行WMIC 时将其解释为文字字符strong> FOR 作为重定向操作符的命令行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-22
    • 1970-01-01
    相关资源
    最近更新 更多