使用批量延迟扩展在路径中出现特殊字符(感叹号!、胡萝卜 ^ 等)
Issue with special characters in path (exclamation point !, carrot ^, etc) using batch Delayed Expansion
我环顾四周,但无法找到任何东西让我的脚本在文件路径中使用特殊字符(例如 !
或 ;
或 ^
)正常工作或文件名。
我的脚本确实有效,但前提是以上字符不在任何扫描的文件夹或文件名中。如果任何文件夹或文件有这些字符,那么脚本就会崩溃。我需要帮助弄清楚如何使我的脚本在路径或文件名中使用特殊字符(如上)。这是我的脚本:
set srcdir=%~dp0%src
set desdir=%~dp0%des
setlocal EnableDelayedExpansion
for /r "%srcdir%" %%f in ("*.txt") do (
set "subdir=%%~f"
set "subdir=!subdir:%srcdir%=%desdir%!"
echo !subdir!
pause
)
endlocal
感谢所有帮助!
- 将所有路径放在
""
. 之间
- 始终使用语法
set "VAR=Value"
.
- 切换延迟扩展:扩展
%%~F
时,禁用它;之后,启用它。
固定代码如下:
setlocal DisableDelayedExpansion
set "srcdir=%~dp0src"
set "desdir=%~dp0des"
for /R "%srcdir%" %%F in ("*.txt") do (
set "subdir=%%~F"
setlocal EnableDelayedExpansion
set "subdir=!subdir:%srcdir%=%desdir%!"
echo(!subdir!
pause
endlocal
)
endlocal
这仅在存储批处理文件的目录不包含感叹号的情况下有效。如果是这样,请告诉我...
修正案
使用变量进行搜索和替换字符串的子字符串替换通常对所有字符都是不安全的。
假设你有这样的东西:
echo(!VAR:%SEARCH%=%REPLACE%!
这意味着将每个出现的 %SEARCH%
替换为 %REPLACE%
(以不区分大小写的方式)。
但是如果 %SEARCH%
包含 =
,行为会改变:例如,%SEARCH%
是 a=b
而 %REPLACE%
是 cd
, 立即扩展的版本是 !VAR:a=b=cd!
, 所以每个 a
将被 b=cd
.
替换
%SEARCH%
中的前导 *
改变了行为:将 %SEARCH%
之前的所有内容替换为 %REPLACE%
。 (当然,路径中不能出现星号。)
%SEARCH%
中的前导~
将子串替换行为改为子串扩展,即扩展由字符位置和长度给定的字符串部分;如果违反语法,将按字面返回未扩展的字符串 !VAR:~a=b!
,假设 ~a
和 b
分别是搜索和替换字符串。
最后,如果%SEARCH%
and/or replace包含一个!
,这将作为延迟扩展的结束!
,所以!VAR:a!=b!
被视为 !VAR:a!
,这是无效语法,将保持原样。
当您 设置 变量时启用延迟扩展时感叹号会被破坏。您可以通过等待延迟扩展直到 检索 变量值来避免这种情况。有时这需要一些杂技才能让它发挥作用。在这种情况下,禁用延迟扩展并使用 call
延迟扩展可能更容易。
@echo off
setlocal
set "srcdir=%~dp0%src"
set "desdir=%~dp0%des"
for /r "%srcdir%" %%f in ("*.txt") do (
set "subdir=%%~f"
rem // use call to avoid delayed expansion and preserve exclamation marks
call set "subdir=%%subdir:%srcdir%=%desdir%%%"
rem // use set /p rather than echo to exploit quotation marks and preserve carets
call set /P "=%%subdir%%"<NUL & echo;
pause
)
或者,如果您更喜欢延迟扩展,我喜欢用来为一行切换延迟扩展的一个技巧是使用 for
循环,如下所示:
@echo off
setlocal
set "srcdir=%~dp0%src"
set "desdir=%~dp0%des"
for /r "%srcdir%" %%f in ("*.txt") do (
set "subdir=%%~f"
setlocal enabledelayedexpansion
for %%I in ("!subdir:%srcdir%=%desdir%!") do endlocal & set "subdir=%%~I" & echo(%%~I
pause
)
我环顾四周,但无法找到任何东西让我的脚本在文件路径中使用特殊字符(例如 !
或 ;
或 ^
)正常工作或文件名。
我的脚本确实有效,但前提是以上字符不在任何扫描的文件夹或文件名中。如果任何文件夹或文件有这些字符,那么脚本就会崩溃。我需要帮助弄清楚如何使我的脚本在路径或文件名中使用特殊字符(如上)。这是我的脚本:
set srcdir=%~dp0%src
set desdir=%~dp0%des
setlocal EnableDelayedExpansion
for /r "%srcdir%" %%f in ("*.txt") do (
set "subdir=%%~f"
set "subdir=!subdir:%srcdir%=%desdir%!"
echo !subdir!
pause
)
endlocal
感谢所有帮助!
- 将所有路径放在
""
. 之间
- 始终使用语法
set "VAR=Value"
. - 切换延迟扩展:扩展
%%~F
时,禁用它;之后,启用它。
固定代码如下:
setlocal DisableDelayedExpansion
set "srcdir=%~dp0src"
set "desdir=%~dp0des"
for /R "%srcdir%" %%F in ("*.txt") do (
set "subdir=%%~F"
setlocal EnableDelayedExpansion
set "subdir=!subdir:%srcdir%=%desdir%!"
echo(!subdir!
pause
endlocal
)
endlocal
这仅在存储批处理文件的目录不包含感叹号的情况下有效。如果是这样,请告诉我...
修正案
使用变量进行搜索和替换字符串的子字符串替换通常对所有字符都是不安全的。
假设你有这样的东西:
echo(!VAR:%SEARCH%=%REPLACE%!
这意味着将每个出现的 %SEARCH%
替换为 %REPLACE%
(以不区分大小写的方式)。
但是如果 %SEARCH%
包含 =
,行为会改变:例如,%SEARCH%
是 a=b
而 %REPLACE%
是 cd
, 立即扩展的版本是 !VAR:a=b=cd!
, 所以每个 a
将被 b=cd
.
%SEARCH%
中的前导 *
改变了行为:将 %SEARCH%
之前的所有内容替换为 %REPLACE%
。 (当然,路径中不能出现星号。)
%SEARCH%
中的前导~
将子串替换行为改为子串扩展,即扩展由字符位置和长度给定的字符串部分;如果违反语法,将按字面返回未扩展的字符串 !VAR:~a=b!
,假设 ~a
和 b
分别是搜索和替换字符串。
最后,如果%SEARCH%
and/or replace包含一个!
,这将作为延迟扩展的结束!
,所以!VAR:a!=b!
被视为 !VAR:a!
,这是无效语法,将保持原样。
当您 设置 变量时启用延迟扩展时感叹号会被破坏。您可以通过等待延迟扩展直到 检索 变量值来避免这种情况。有时这需要一些杂技才能让它发挥作用。在这种情况下,禁用延迟扩展并使用 call
延迟扩展可能更容易。
@echo off
setlocal
set "srcdir=%~dp0%src"
set "desdir=%~dp0%des"
for /r "%srcdir%" %%f in ("*.txt") do (
set "subdir=%%~f"
rem // use call to avoid delayed expansion and preserve exclamation marks
call set "subdir=%%subdir:%srcdir%=%desdir%%%"
rem // use set /p rather than echo to exploit quotation marks and preserve carets
call set /P "=%%subdir%%"<NUL & echo;
pause
)
或者,如果您更喜欢延迟扩展,我喜欢用来为一行切换延迟扩展的一个技巧是使用 for
循环,如下所示:
@echo off
setlocal
set "srcdir=%~dp0%src"
set "desdir=%~dp0%des"
for /r "%srcdir%" %%f in ("*.txt") do (
set "subdir=%%~f"
setlocal enabledelayedexpansion
for %%I in ("!subdir:%srcdir%=%desdir%!") do endlocal & set "subdir=%%~I" & echo(%%~I
pause
)