解决带有“!”的问题具有延迟变量扩展的 For 循环中的文字
Workaround for problems with "!" literals in a For loop with delayed variable expansion
如何解决 FOR 循环?问题是对于带有“!”的文件名,DelayedExpansion 失败。在他们
代码(这是更大脚本的一部分)
:FileScan
::Sets the filenames(in alphabetical order) to variables
SETLOCAL EnableDelayedExpansion
SET "EpCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
SET Ep!EpCount!=%%x
)
IF %EpCount%==0 ( Echo. & Echo: No Episodes Found & GOTO :Err )
:FolderScan
::Sets the foldernames(in alphabetical order) to variables
SET "FolderCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n /a:d "%~dp0Put_Your_Files_Here" 2^>nul') DO (
SET /a "FolderCount+=1"
SET Folder!FolderCount!=%%x
)
如果不能批量处理,我该如何在 PowerShell 中完成
到原始批次可以调用的东西,例如:
:FileScan
Call %~dp0FileScanScript.ps1
IF %EpCount%==0 ( Echo. & Echo: No Episodes Found & GOTO :Err )
:FolderScan
Call %~dp0FolderScanScript.ps1
编辑:CALL SET 修复了原始代码,完全避免了 DelayedExpansion
SET "EpCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
CALL SET "Ep%%EpCount%%=%%x"
)
注意:OP 最初标记了问题 powershell in addition to batch-file,但后来删除了该标记(已恢复)。
等效于以下批处理文件 (cmd
) 循环:
SETLOCAL EnableDelayedExpansion
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
SET Ep!EpCount!=%%x
)
在 PowerShell (PSv3+) 中是:
# Collect all episode paths in array $Eps
$Eps = (Get-ChildItem -Recurse $PSScriptRoot -Include *.mkv, *.mp4, *.avi, *.flv).FullName
# Count the number of paths collected.
$EpCount = $Eps.Count
以上内容也适用于嵌入了 !
个实例的路径。
$Eps
将是完整文件名的 数组 。
然后一个一个处理匹配的文件:
foreach ($ep in $Eps) { # Loop over all files (episodes)
# Work with $ep
}
使用单个 array 而不是不同的 $Ep1
, $Ep2
, ...变量使得处理更简单和更有效。
以上是对已经收集到内存中的文件名进行处理的最快方法
内存效率更高(尽管速度稍慢)的基于管道的方法 将是:
Get-ChildItem -Recurse $PSScriptRoot -Include *.mkv,*.mp4,*.avi,*.flv | ForEach-Object {
# Work with $_.FullName - automatic variable $_ represents the input object at hand.
}
CALL SET Ep%%EpCount%%=%%x
会像
一样适当地设置你的变量
FOR /f "tokens=1*delims=[]" %%x IN (
'Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul ^|find /v /n ""'
) DO (
SET /a epcount=%%x
CALL SET ep%%x=%%y
)
(在每个名称前加上 [num]
然后使用 for
将 num
提取到 %%x
并将 name
提取到 %%y
(假设有没有以 [
或 ]
))
开头的名称
最简单的解决方案确实是使用call
set
引入另一个解析阶段:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
set /A "EpCount+=1"
call set "Ep%%EpCount%%=%%x"
)
popd
endlocal
但是,一旦插入符 ^
出现在字符串或文件名中,这就会引起问题,因为 call
在它们出现时加倍引用。
为了解决这个问题,你需要确保字符串在第二个解析阶段也被扩展,这可以通过使用一个中间变量来实现,像这样:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
set "Episode=%%x"
set /A "EpCount+=1"
call set "Ep%%EpCount%%=%%Episode%%"
)
popd
set Ep
endlocal
另外,我在dir
to ensure that no directories are returned. Furthermore, I used pushd
and popd
中添加了过滤选项/A:-D
,临时切换到脚本的父目录。您编写的 dir
命令行在脚本的父目录中搜索文件 *.mkv*
,但在当前工作目录中搜索所有其他文件,这可能不是您想要的。
另一个选项是切换延迟扩展,这样 for
变量引用 %%x
在禁用延迟扩展时会扩展,并且分配给 Ep
数组样式变量启用时完成。但是您需要采取措施将变量值传输到 endlocal
障碍之外,如以下示例所示:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
rem /* Delayed expansion is disabled at this point, so expanding
rem the `for` variable reference `%%x` is safe here: */
set "Episode=%%x"
set /A "EpCount+=1"
rem // Enable delayed expansion here:
setlocal EnableDelayedExpansion
rem /* Use a `for /F` loop that iterates once only over the assignment string,
rem which cannot be empty, so the loop always iterates; the expanded value
rem is stored in `%%z`, which must be assigned after `endlocal` in order
rem to have it available afterwards and not to lose exclamation marks: */
for /F "delims=" %%z in ("Ep!EpCount!=!Episode!") do (
endlocal
set "%%z"
)
)
popd
set "Ep"
endlocal
如何解决 FOR 循环?问题是对于带有“!”的文件名,DelayedExpansion 失败。在他们
代码(这是更大脚本的一部分)
:FileScan
::Sets the filenames(in alphabetical order) to variables
SETLOCAL EnableDelayedExpansion
SET "EpCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
SET Ep!EpCount!=%%x
)
IF %EpCount%==0 ( Echo. & Echo: No Episodes Found & GOTO :Err )
:FolderScan
::Sets the foldernames(in alphabetical order) to variables
SET "FolderCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n /a:d "%~dp0Put_Your_Files_Here" 2^>nul') DO (
SET /a "FolderCount+=1"
SET Folder!FolderCount!=%%x
)
如果不能批量处理,我该如何在 PowerShell 中完成 到原始批次可以调用的东西,例如:
:FileScan
Call %~dp0FileScanScript.ps1
IF %EpCount%==0 ( Echo. & Echo: No Episodes Found & GOTO :Err )
:FolderScan
Call %~dp0FolderScanScript.ps1
编辑:CALL SET 修复了原始代码,完全避免了 DelayedExpansion
SET "EpCount=0"
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
CALL SET "Ep%%EpCount%%=%%x"
)
注意:OP 最初标记了问题 powershell in addition to batch-file,但后来删除了该标记(已恢复)。
等效于以下批处理文件 (cmd
) 循环:
SETLOCAL EnableDelayedExpansion
FOR /f "Tokens=* Delims=" %%x IN ('Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul') DO (
SET /a "EpCount+=1"
SET Ep!EpCount!=%%x
)
在 PowerShell (PSv3+) 中是:
# Collect all episode paths in array $Eps
$Eps = (Get-ChildItem -Recurse $PSScriptRoot -Include *.mkv, *.mp4, *.avi, *.flv).FullName
# Count the number of paths collected.
$EpCount = $Eps.Count
以上内容也适用于嵌入了 !
个实例的路径。
$Eps
将是完整文件名的 数组 。
然后一个一个处理匹配的文件:
foreach ($ep in $Eps) { # Loop over all files (episodes)
# Work with $ep
}
使用单个 array 而不是不同的 $Ep1
, $Ep2
, ...变量使得处理更简单和更有效。
以上是对已经收集到内存中的文件名进行处理的最快方法
内存效率更高(尽管速度稍慢)的基于管道的方法 将是:
Get-ChildItem -Recurse $PSScriptRoot -Include *.mkv,*.mp4,*.avi,*.flv | ForEach-Object {
# Work with $_.FullName - automatic variable $_ represents the input object at hand.
}
CALL SET Ep%%EpCount%%=%%x
会像
一样适当地设置你的变量FOR /f "tokens=1*delims=[]" %%x IN (
'Dir /b /o:n %~dp0*.mkv* *.mp4* *.avi* *.flv* 2^>nul ^|find /v /n ""'
) DO (
SET /a epcount=%%x
CALL SET ep%%x=%%y
)
(在每个名称前加上 [num]
然后使用 for
将 num
提取到 %%x
并将 name
提取到 %%y
(假设有没有以 [
或 ]
))
最简单的解决方案确实是使用call
set
引入另一个解析阶段:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
set /A "EpCount+=1"
call set "Ep%%EpCount%%=%%x"
)
popd
endlocal
但是,一旦插入符 ^
出现在字符串或文件名中,这就会引起问题,因为 call
在它们出现时加倍引用。
为了解决这个问题,你需要确保字符串在第二个解析阶段也被扩展,这可以通过使用一个中间变量来实现,像这样:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
set "Episode=%%x"
set /A "EpCount+=1"
call set "Ep%%EpCount%%=%%Episode%%"
)
popd
set Ep
endlocal
另外,我在dir
to ensure that no directories are returned. Furthermore, I used pushd
and popd
中添加了过滤选项/A:-D
,临时切换到脚本的父目录。您编写的 dir
命令行在脚本的父目录中搜索文件 *.mkv*
,但在当前工作目录中搜索所有其他文件,这可能不是您想要的。
另一个选项是切换延迟扩展,这样 for
变量引用 %%x
在禁用延迟扩展时会扩展,并且分配给 Ep
数组样式变量启用时完成。但是您需要采取措施将变量值传输到 endlocal
障碍之外,如以下示例所示:
setlocal DisableDelayedExpansion
pushd "%~dp0."
set /A "EpCount=0"
for /F "delims=" %%x in ('
dir /B /A:-D /O:N "*.mkv*" "*.mp4*" "*.avi*" "*.flv*" 2^> nul
') do (
rem /* Delayed expansion is disabled at this point, so expanding
rem the `for` variable reference `%%x` is safe here: */
set "Episode=%%x"
set /A "EpCount+=1"
rem // Enable delayed expansion here:
setlocal EnableDelayedExpansion
rem /* Use a `for /F` loop that iterates once only over the assignment string,
rem which cannot be empty, so the loop always iterates; the expanded value
rem is stored in `%%z`, which must be assigned after `endlocal` in order
rem to have it available afterwards and not to lose exclamation marks: */
for /F "delims=" %%z in ("Ep!EpCount!=!Episode!") do (
endlocal
set "%%z"
)
)
popd
set "Ep"
endlocal