【问题标题】:Windows command working in cmd but not .bat fileWindows 命令在 cmd 但不是 .bat 文件中工作
【发布时间】:2020-06-24 09:06:22
【问题描述】:

我正在尝试替换某些文件中的一行(基于文件扩展名)。该程序未按预期运行,导致问题的命令是以下命令。

FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^"') DO (

这会返回以下错误:

FINDSTR: No search strings 进程试图写入一个不存在的 管道。

但是,在命令行中运行时,命令本身可以正常工作。我已经花了将近一整天的时间,但无济于事。 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 ..

【问题讨论】:

  • 也许尝试用('FINDSTR /N "^" !FILE!') 替换('TYPE !FILE! ^| FINDSTR /N "^"')
  • @PhilBrubaker,这不会改变任何事情;问题是启用了延迟扩展,即使引用了^,它也会消耗它......

标签: windows batch-file cmd delayedvariableexpansion


【解决方案1】:
只要您的源运行文件没有任何以 : 字符开头的行,以下可能提供执行任务的替代方法:
@%__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 开头的行.RUN FILE login.txt;

【讨论】:

    【解决方案2】:

    FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^"') DO ( 行中的问题是您启用了delayed variable expansion,即使在被引用时也会使用插入符号^ 进行转义。详情另请参阅此帖:How does the Windows Command Interpreter (CMD.EXE) parse scripts?

    要解决此问题,您只需将插入符号加倍:

    FOR /F %%k IN ('TYPE !FILE! ^| FINDSTR /N "^^"') DO (
    

    请注意,将$ 指定为findstr 的搜索字符串会跳过输入数据的最后一行,以防它没有被换行符终止。另请注意,$ 锚定回车字符,该字符仅存在于带有 Windows 样式的行尾标记回车加换行符的文本文件中。


    无论如何,这是您代码的固定变体,它尽可能避免延迟扩展,因此实际上不需要加倍插入符号:

    @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
    

    【讨论】:

    • 这行得通 - 感谢您为每一行代码提供如此详细的注释。与阅读文档相比,我在这里学到了更多关于命令的知识 :) 查看代码,我相信 delayedvariableexpansion 可以完全跳过。我已经使用您提供的逻辑提供了替代解决方案。
    • 不客气!关于延迟扩展:需要安全回显删除前行号加:的文本行;你当然可以只做echo(%%M 代替(没有延迟扩展),但这会从行中删除前导冒号......
    【解决方案3】:

    感谢菲尔的建议,但没有奏效。

    奇怪的是,我尝试使用行尾字符 $ 而不是行首 ^ 并且似乎成功了。

    似乎 ^ 被视为文字,甚至用 \ 转义它也无法按预期工作。

    第一个工作解决方案

    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
    )
    

    【讨论】:

    • stariq,你还没有调整你的代码和对较低答案的注释以相互匹配!,(使用右字边界\&gt;remark)。顺便说一句,我的答案也不需要或使用延迟扩展,并且由于它只嵌套一个for-loop,它可能比嵌套两个或三个的答案更有效。此外,在为您制定并发布解决方案后,您至少可以对其进行测试并提供反馈。
    • 感谢您指出备注和代码中的差异 - 已修复。另外,很抱歉没有对您提出的解决方案发表评论。它确实第一次起作用(因此将其标记为有用)。我仍处于学习批处理命令的早期阶段,对我来说很难遵循。基于我对代码的有限理解,我采用了最终的解决方案。我想你的代码很高效,但文件集真的很难在执行时间差异上拾取。
    • 你是对的!这是每个人都可以使用的完美工作解决方案。并不是有意忽视您的努力,如果结果如此,请道歉。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-28
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多