Windows 命令在 cmd 中有效,但在 .bat 文件中无效
Windows command working in cmd but not .bat file
我正在尝试替换某些文件中的一行(基于文件扩展名)。该程序未按预期运行,导致问题的命令如下。
FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^"') DO (
返回时出现以下错误:
FINDSTR: No search strings The process tried to write to a nonexistent
pipe.
但是,当在命令行中 运行 时,命令本身按预期工作。我已经花了将近 1 整天,但无济于事。
FOR /F %k IN ('TYPE <filename> ^| FINDSTR /N "^"') DO echo(%k
不胜感激!
完整代码如下,供参考。
@echo off
CD data
FOR /F "delims=" %%i IN ('DIR *.ext1 /B') DO (
SET "FILE=%%i"
SETLOCAL EnableDelayedExpansion
echo(!FILE!
<!FILE! >!FILE!.tmp~ (
REM Find line number on which Logon command is found
FOR /F "tokens=1,* delims=: " %%j IN ('FINDSTR /I /N /R "^\.LOGON.*" !FILE!') DO (
SET "NUM=%%j"
)
REM Print all lines along with line number at beginning
FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^"') DO (
SET "LINE=%%k"
REM Replace entire content of Logon line with Run file command
FOR /F "tokens=1,* delims=:" %%l IN ("!LINE!") DO IF %%l EQU !NUM! (
echo(.RUN FILE logon.txt;
) ELSE (
echo(!LINE:*:=!
)
)
)
MOVE /Y "!FILE!.tmp~" !%FILE!"
ENDLOCAL
)
CD ..
感谢菲尔的建议,但没有奏效。
奇怪的是,我尝试使用行尾字符 $ 而不是行首字符 ^ 并且它似乎已经成功了。
似乎 ^ 被当作字面意思,甚至用 \ 转义它也没有按预期工作。
第一个可行的解决方案
FOR /F "delims=" %%i IN ('DIR *.ext1 /B') DO (
SET "FILE=%%i"
SETLOCAL EnableDelayedExpansion
<!FILE! >!FILE!.tmp~ (
REM Find line number on which Logon command is found
FOR /F "tokens=1,* delims=:" %%j IN ('FINDSTR /I /N /R "^\.LOGON.*" !FILE!') DO (
SET "NUM=%%j"
)
REM Print all lines along with line number at beginning
FOR /F "tokens=1,* delims=" %%k IN ('FINDSTR /N "$" !FILE!') DO (
SET "LINE=%%k"
REM Replace entire content of Logon line with Run file command
FOR /F "tokens=1,* delims=:" %%l IN ("!LINE!") DO IF %%l EQU !NUM! (
echo(.RUN FILE logon.txt;
) ELSE (
echo(!LINE:*:=!
)
)
)
MOVE /Y !FILE!.tmp~ !FILE!
echo Auto-generated !FILE!
ENDLOCAL
)
修改后的工作解决方案(感谢 aschipfl)
@echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0data" && (
rem // Loop through all files with .mload file extension:
for /F "delims=" %%I in ('dir /B /A:-D-H-S "*.mload"') do (
< "%%I" > "%%I.tmp~" (
rem // Use beginning of line position with .logon in the search string:
for /F "tokens=1,* delims=:" %%J in ('findstr /I /N /R "^\.logon" "%%I"') do (
rem // Use beginning of line position in the search string:
for /F "delims=" %%K in ('type "%%I" ^| findstr /N "^"') do (
rem // Match current line number with previously searched line:
for /F "tokens=1,* delims=:" %%L in ("%%K") do if %%L equ %%J (
echo(.RUN FILE logon.txt;
) else (
echo(%%M
)
)
)
)
> nul move /Y "%%I.tmp~" "%%I"
)
popd
)
第 FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^"') DO (
行的问题是您得到了 delayed variable expansion enabled, which also consumes the caret symbol ^
for escaping even when being quoted. Refer also to this post for details: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
要解决此问题,您只需将插入符号加倍即可:
FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^^"') DO (
请注意,指定 $
作为 findstr
的搜索字符串会跳过输入数据的最后一行,以防它没有被换行符终止。另请注意,$
锚定到 carriage-return 字符,该字符仅出现在带有 Windows 样式行尾标记 carriage-return 的文本文件中换行。
无论如何,这是你的代码的一个固定变体,它尽可能避免延迟扩展,因此实际上不需要加倍插入符号:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // It is assumed here that the parent directory of the script is the root location:
pushd "%~dp0data" && (
for /F "delims=" %%I in ('dir /B /A:-D-H-S "*.ext1"') do (
set "FILE=%%I"
echo(%%I
rem // Here `%%I` is used instead of `!FILE!` since delayed expansion is disabled:
< "%%I" > "%%I.tmp~" (
rem // Use right word boundary `\>` in the search string:
for /F "tokens=1,* delims=:" %%J in ('findstr /I /N /R "^\.LOGON\>" "%%I"') do (
rem /* Since this loop should iterate once only anyway, the interim variable
rem `NUM` is actually not really needed when the remaining code is also
rem placed within the loop body: */
rem set "NUM=%%J"
rem // At this point delayed expansion is still disabled:
for /F %%K in ('type "%%I" ^| findstr /N "^"') do (
set "LINE=%%K"
rem // Here `%%J` is used instead of `!NUM!`:
for /F "tokens=1,* delims=:" %%L in ("!LINE!") do if %%L equ %%J (
echo(.RUN FILE logon.txt;
) else (
rem // This is the only part where delayed expansion is needed:
setlocal EnableDelayedExpansion
echo(!LINE:*:=!
endlocal
)
)
)
)
> nul move /Y "%%I.tmp~" "%%I"
)
popd
)
endlocal
exit /B
只要您的源 运行 文件没有任何以 : 字符开头的行,以下内容可能会提供执行该任务的替代方法:
@%__AppDir__%where.exe /Q "data":"*.mload" >NUL 2>&1 && (CD "data"
For /F "EOL=? Delims=" %%H In (
'%__AppDir__%findstr.exe /IM "\<\.LOGON\>" "*.mload" 2^>NUL'
) Do @(Copy /Y "%%H" "%%~nH.tmp~" >NUL && (
For /F "Tokens=1,* Delims=:" %%I In (
'%__AppDir__%findstr.exe /N "^" "%%~nH.tmp~"'
) Do @Set /P "=:%%J"<NUL|%__AppDir__%findstr.exe /LIB ":.LOGON " >NUL && (
Echo .RUN FILE logon.txt;) || Echo=%%J)>"%%H"
Del "%%~nH.tmp~" 2>NUL))
明确地说,我对您的要求的解读是,替换 .\data
内所有 .mload
文件中的所有行,这些行以不区分大小写的字符串 .LOGON 开头,行 .运行 FILE logon.txt;
我正在尝试替换某些文件中的一行(基于文件扩展名)。该程序未按预期运行,导致问题的命令如下。
FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^"') DO (
返回时出现以下错误:
FINDSTR: No search strings The process tried to write to a nonexistent pipe.
但是,当在命令行中 运行 时,命令本身按预期工作。我已经花了将近 1 整天,但无济于事。
FOR /F %k IN ('TYPE <filename> ^| FINDSTR /N "^"') DO echo(%k
不胜感激!
完整代码如下,供参考。
@echo off
CD data
FOR /F "delims=" %%i IN ('DIR *.ext1 /B') DO (
SET "FILE=%%i"
SETLOCAL EnableDelayedExpansion
echo(!FILE!
<!FILE! >!FILE!.tmp~ (
REM Find line number on which Logon command is found
FOR /F "tokens=1,* delims=: " %%j IN ('FINDSTR /I /N /R "^\.LOGON.*" !FILE!') DO (
SET "NUM=%%j"
)
REM Print all lines along with line number at beginning
FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^"') DO (
SET "LINE=%%k"
REM Replace entire content of Logon line with Run file command
FOR /F "tokens=1,* delims=:" %%l IN ("!LINE!") DO IF %%l EQU !NUM! (
echo(.RUN FILE logon.txt;
) ELSE (
echo(!LINE:*:=!
)
)
)
MOVE /Y "!FILE!.tmp~" !%FILE!"
ENDLOCAL
)
CD ..
感谢菲尔的建议,但没有奏效。
奇怪的是,我尝试使用行尾字符 $ 而不是行首字符 ^ 并且它似乎已经成功了。
似乎 ^ 被当作字面意思,甚至用 \ 转义它也没有按预期工作。
第一个可行的解决方案
FOR /F "delims=" %%i IN ('DIR *.ext1 /B') DO (
SET "FILE=%%i"
SETLOCAL EnableDelayedExpansion
<!FILE! >!FILE!.tmp~ (
REM Find line number on which Logon command is found
FOR /F "tokens=1,* delims=:" %%j IN ('FINDSTR /I /N /R "^\.LOGON.*" !FILE!') DO (
SET "NUM=%%j"
)
REM Print all lines along with line number at beginning
FOR /F "tokens=1,* delims=" %%k IN ('FINDSTR /N "$" !FILE!') DO (
SET "LINE=%%k"
REM Replace entire content of Logon line with Run file command
FOR /F "tokens=1,* delims=:" %%l IN ("!LINE!") DO IF %%l EQU !NUM! (
echo(.RUN FILE logon.txt;
) ELSE (
echo(!LINE:*:=!
)
)
)
MOVE /Y !FILE!.tmp~ !FILE!
echo Auto-generated !FILE!
ENDLOCAL
)
修改后的工作解决方案(感谢 aschipfl)
@echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0data" && (
rem // Loop through all files with .mload file extension:
for /F "delims=" %%I in ('dir /B /A:-D-H-S "*.mload"') do (
< "%%I" > "%%I.tmp~" (
rem // Use beginning of line position with .logon in the search string:
for /F "tokens=1,* delims=:" %%J in ('findstr /I /N /R "^\.logon" "%%I"') do (
rem // Use beginning of line position in the search string:
for /F "delims=" %%K in ('type "%%I" ^| findstr /N "^"') do (
rem // Match current line number with previously searched line:
for /F "tokens=1,* delims=:" %%L in ("%%K") do if %%L equ %%J (
echo(.RUN FILE logon.txt;
) else (
echo(%%M
)
)
)
)
> nul move /Y "%%I.tmp~" "%%I"
)
popd
)
第 FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^"') DO (
行的问题是您得到了 delayed variable expansion enabled, which also consumes the caret symbol ^
for escaping even when being quoted. Refer also to this post for details: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
要解决此问题,您只需将插入符号加倍即可:
FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^^"') DO (
请注意,指定 $
作为 findstr
的搜索字符串会跳过输入数据的最后一行,以防它没有被换行符终止。另请注意,$
锚定到 carriage-return 字符,该字符仅出现在带有 Windows 样式行尾标记 carriage-return 的文本文件中换行。
无论如何,这是你的代码的一个固定变体,它尽可能避免延迟扩展,因此实际上不需要加倍插入符号:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // It is assumed here that the parent directory of the script is the root location:
pushd "%~dp0data" && (
for /F "delims=" %%I in ('dir /B /A:-D-H-S "*.ext1"') do (
set "FILE=%%I"
echo(%%I
rem // Here `%%I` is used instead of `!FILE!` since delayed expansion is disabled:
< "%%I" > "%%I.tmp~" (
rem // Use right word boundary `\>` in the search string:
for /F "tokens=1,* delims=:" %%J in ('findstr /I /N /R "^\.LOGON\>" "%%I"') do (
rem /* Since this loop should iterate once only anyway, the interim variable
rem `NUM` is actually not really needed when the remaining code is also
rem placed within the loop body: */
rem set "NUM=%%J"
rem // At this point delayed expansion is still disabled:
for /F %%K in ('type "%%I" ^| findstr /N "^"') do (
set "LINE=%%K"
rem // Here `%%J` is used instead of `!NUM!`:
for /F "tokens=1,* delims=:" %%L in ("!LINE!") do if %%L equ %%J (
echo(.RUN FILE logon.txt;
) else (
rem // This is the only part where delayed expansion is needed:
setlocal EnableDelayedExpansion
echo(!LINE:*:=!
endlocal
)
)
)
)
> nul move /Y "%%I.tmp~" "%%I"
)
popd
)
endlocal
exit /B
@%__AppDir__%where.exe /Q "data":"*.mload" >NUL 2>&1 && (CD "data"
For /F "EOL=? Delims=" %%H In (
'%__AppDir__%findstr.exe /IM "\<\.LOGON\>" "*.mload" 2^>NUL'
) Do @(Copy /Y "%%H" "%%~nH.tmp~" >NUL && (
For /F "Tokens=1,* Delims=:" %%I In (
'%__AppDir__%findstr.exe /N "^" "%%~nH.tmp~"'
) Do @Set /P "=:%%J"<NUL|%__AppDir__%findstr.exe /LIB ":.LOGON " >NUL && (
Echo .RUN FILE logon.txt;) || Echo=%%J)>"%%H"
Del "%%~nH.tmp~" 2>NUL))
明确地说,我对您的要求的解读是,替换 .\data
内所有 .mload
文件中的所有行,这些行以不区分大小写的字符串 .LOGON 开头,行 .运行 FILE logon.txt;