使用 windows 命令行增加文件夹名称编号
Increment folders names numbering using windows command line
每周我都会创建一个新文件夹,为了保持它们的顺序,我用 01 - day-month
、02 - 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。
@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
,它选择 /xri
ex 的行实际上,case-I不敏感地匹配提供的R正则表达式。
原来的regex
,“[0-9]%baseName%”不够用。它试图说 "any number of leading digits (including no leading digits at all)" 后跟 basename
中的字符串,然后是 *
这意味着 "any number of the last character in basename
"
此外,由于 basename
包含 Space,findstr 将每个 space 分隔的子字符串视为一个单独的字符串来查找,并将因此尝试查找并精确匹配 "any number of digits" OR "-" OR "*",所以没有结果也就不足为奇了。
替换 regex
首先是 /c:
,这意味着 "spaces are ordinary characters" 正则表达式本身然后指定两个前导数字, basename
字符串和 .*
- 任意数量的任何其他字符(因为您要检查的目录名将被重命名为 02 - day-month
)。
通过将 delims
设置为 space,分配给 %%F
的(隐式默认单个标记)将是列表中目录名中的任何字符(在 findstr
过滤了它)直到但不包括第一个 Space
所以,现在剩下要做的就是找到%%F
中的最高数值。这是通过将 %%F
与 n
的值进行比较(以后请使用有意义的名称)并将 n
设置为 %%F
的值来完成的,如果 那更大。
!n!
语法是必需的,因为 delayed expansion
特性(有很多很多关于这个的 SO 文章 - 使用顶部栏中的 search
找到一些) .本质上,在一个代码块(带括号的一系列命令)中,任何 %var%
都被遇到该块时的变量值替换,并且 !var!
(调用 delayedexpansion
)是运行-时间值(随着块的运行而变化的值)
循环结束后,我们会遇到以下两种情况之一:要么没有匹配(因此 n
将具有值 0
),要么存在匹配,因此 n
将值为 nn
,即 2 位数字)
set /a
增加 n
中的值,这一切都很好 - EXCEPT 如果 n
恰好是 08
或 09
,操作将失败,因为 cmd
看到以 0
开头的数字字符串为 octal 和 8
并且9
不是有效的八进制字符。
此问题的标准解决方法是在两位数的值前添加一个 1
。显然,如果 n
是 0
,那么向其添加 1
将得到 1
- 并且您需要一个前导零,因此在这种情况下,将 n
设置为100
。 (我使用了强制算术 set
)
增加数字后,md
现在需要 n
的最后 2 个字符,这就是 %n:~-2%
的含义。我刚刚 echo
编辑了提议的新名称,以防出现问题。在测试激活 md
.
后,只需删除 echo
关键字
最好是剪切和粘贴解决方案,因为批处理可能会表现出奇怪的语法敏感性。它还可以避免错误。
这是我的解决方案,它使用了一种不同的方法,除了执行 WMIC 命令之外速度很快,当应该从中确定当前日期和月份时,这是无法避免的local date/time 以独立于区域和语言的方式。重新格式化环境变量 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%"
编辑: 通过在文件夹名称的数字字符串前面加上 1
来避免对带有前导 0 的数字进行八进制解释的方法,如 [=30= 所解释的那样] 很棒,让代码更简单,速度也更快:
@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
定义文件夹名称在文件夹和文件名中,因为使用这种国际日期格式,按名称排序的文件和文件夹也会自动按日期排序。
所以当不需要在同一天创建多个文件夹而确实需要文件夹名称中的递增数字时,文件夹名称中使用国际日期格式会更好,批代码可以减少为:
@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%
要了解使用的命令及其工作原理,请打开命令提示符 window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。
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 命令行时将其解释为文字字符然后通过 FOR 作为重定向运算符执行 WMIC 命令行。
每周我都会创建一个新文件夹,为了保持它们的顺序,我用 01 - day-month
、02 - 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。
@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
,它选择 /xri
ex 的行实际上,case-I不敏感地匹配提供的R正则表达式。
原来的regex
,“[0-9]%baseName%”不够用。它试图说 "any number of leading digits (including no leading digits at all)" 后跟 basename
中的字符串,然后是 *
这意味着 "any number of the last character in basename
"
此外,由于 basename
包含 Space,findstr 将每个 space 分隔的子字符串视为一个单独的字符串来查找,并将因此尝试查找并精确匹配 "any number of digits" OR "-" OR "*",所以没有结果也就不足为奇了。
替换 regex
首先是 /c:
,这意味着 "spaces are ordinary characters" 正则表达式本身然后指定两个前导数字, basename
字符串和 .*
- 任意数量的任何其他字符(因为您要检查的目录名将被重命名为 02 - day-month
)。
通过将 delims
设置为 space,分配给 %%F
的(隐式默认单个标记)将是列表中目录名中的任何字符(在 findstr
过滤了它)直到但不包括第一个 Space
所以,现在剩下要做的就是找到%%F
中的最高数值。这是通过将 %%F
与 n
的值进行比较(以后请使用有意义的名称)并将 n
设置为 %%F
的值来完成的,如果 那更大。
!n!
语法是必需的,因为 delayed expansion
特性(有很多很多关于这个的 SO 文章 - 使用顶部栏中的 search
找到一些) .本质上,在一个代码块(带括号的一系列命令)中,任何 %var%
都被遇到该块时的变量值替换,并且 !var!
(调用 delayedexpansion
)是运行-时间值(随着块的运行而变化的值)
循环结束后,我们会遇到以下两种情况之一:要么没有匹配(因此 n
将具有值 0
),要么存在匹配,因此 n
将值为 nn
,即 2 位数字)
set /a
增加 n
中的值,这一切都很好 - EXCEPT 如果 n
恰好是 08
或 09
,操作将失败,因为 cmd
看到以 0
开头的数字字符串为 octal 和 8
并且9
不是有效的八进制字符。
此问题的标准解决方法是在两位数的值前添加一个 1
。显然,如果 n
是 0
,那么向其添加 1
将得到 1
- 并且您需要一个前导零,因此在这种情况下,将 n
设置为100
。 (我使用了强制算术 set
)
增加数字后,md
现在需要 n
的最后 2 个字符,这就是 %n:~-2%
的含义。我刚刚 echo
编辑了提议的新名称,以防出现问题。在测试激活 md
.
echo
关键字
最好是剪切和粘贴解决方案,因为批处理可能会表现出奇怪的语法敏感性。它还可以避免错误。
这是我的解决方案,它使用了一种不同的方法,除了执行 WMIC 命令之外速度很快,当应该从中确定当前日期和月份时,这是无法避免的local date/time 以独立于区域和语言的方式。重新格式化环境变量 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%"
编辑: 通过在文件夹名称的数字字符串前面加上 1
来避免对带有前导 0 的数字进行八进制解释的方法,如 [=30= 所解释的那样] 很棒,让代码更简单,速度也更快:
@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
定义文件夹名称在文件夹和文件名中,因为使用这种国际日期格式,按名称排序的文件和文件夹也会自动按日期排序。
所以当不需要在同一天创建多个文件夹而确实需要文件夹名称中的递增数字时,文件夹名称中使用国际日期格式会更好,批代码可以减少为:
@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%
要了解使用的命令及其工作原理,请打开命令提示符 window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。
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 命令行时将其解释为文字字符然后通过 FOR 作为重定向运算符执行 WMIC 命令行。