How to use Windows CMD pipe( | ) feature with CALL :Label command option?
当我想在Windows的CMD shell的CALL:Label选项中使用pipe(|)功能时,我遇到了一个令人沮丧的问题。我有一个非常小的示例(如下):call-test.cmd和示例输出。
问题的根源是将CMD脚本的输出传递到另一个程序,例如tee实用程序或find命令。例如:
1 | @call :Label-02 param | tee call-test.log |
它将在标签Label-02处启动当前命令文件,并将输出通过管道传输到tee。不幸的是,在带有" call:label"选项的行上使用竖线字符(|)会产生错误:
1 | Invalid attempt to call batch label outside of batch script. |
而" call example.cmd | tee example.log"工作正常。
另一个IO重定向>正常。这只是使用" call:label pipe(|)"失败的一种情况。对我来说,它就像一个Windows错误。
是否有人有解决方法和/或知道解释?
谢谢,
将
-
通话测试输出
1
2
3
4
5
6
7c:\> call-test
[start]
label 03 :: p1
Invalid attempt to call batch label outside of batch script.
Invalid attempt to call batch label outside of batch script.
[done]
Press any key to continue . . . -
通话测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29@echo off
@rem call-test.cmd
@rem _________________________________________________
@rem Test :label call option for .cmd files.
@rem
@echo ^ [start]
@call :Label-03 p1
@call :Label-02 second | find""
@call :Label-02 second | tee call-test.log
@goto Done
@rem _________________________________________________
:Label-01
@echo ^ label 01 :: %1
@goto Exit
@rem _________________________________________________
:Label-02
@echo ^ label 02 :: %1
@goto Exit
@rem _________________________________________________
:Label-03
@echo ^ label 03 :: %1
@goto Exit
@rem _________________________________________________
:Done
@echo ^ [done]
@pause
@rem _________________________________________________
:Exit
@exit /b
原因是,管道在cmd上下文中开始于两侧(都在一个cmd框中并行运行),并且每一侧都被解释为真正的命令行参数,并且在cmd上不允许使用行标签。
但是,如果重新启动批处理,则可以调用函数。
1 2 | if not"%1"=="" goto %1 @call"%~0" :Label-02 param | tee call-test.log |
编辑:完整的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | @echo off if not"%~1"=="START" goto :normalStart shift shift call %0 %1 %2 %3 %4 %5 %6 %7 %8 exit /b :normalStart rem call-test.cmd rem _________________________________________________ rem Test :label call option for .cmd files. rem echo ^ [start] rem call :Label-03 p1 rem call :Label-02 second | find"" call"%~dpf0""START" :Label-02 second | tee call-test.log goto Done rem _________________________________________________ :Label-01 echo ^ label 01 :: %1 goto Exit rem _________________________________________________ :Label-02 echo ^ label 02 :: %1 goto Exit rem _________________________________________________ :Label-03 echo ^ label 03 :: %1 goto Exit rem _________________________________________________ :Done echo ^ [done] pause rem _________________________________________________ :Exit exit /b |
我意识到这有点晚了,但可能会对其他人有所帮助。这不是一个hack,更多的解决方法。如果需要的话,还是漂亮的"不错的工具"。
我正在使用以下解决方案来解决类似问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | @echo off SET CURRENT_SCRIPT_IS=%~dpnx0 IF NOT"%RUN_TO_LABEL%" =="" ( call :%RUN_TO_LABEL% %1 %2 %3 %4 %5 %6 %7 %8 %9 goto:eof ) goto over_debug_stuff :debugstr echo %~1 echo %~1>>%2 goto:eof :debuglbl SET RUN_TO_LABEL=%1 for /f"tokens=*" %%L in ('%CURRENT_SCRIPT_IS% %3 %4 %5 %6 %7 %8 %9') do ( echo %%L echo %%L>>%2 ) SET RUN_TO_LABEL= goto:eof :over_debug_stuff call :debugstr"this is a string""test_str.txt" call :debuglbl tst"test_lbl.txt" goto:eof :tst echo label line 1 echo label line 2 echo label line 3 goto:eof |
关于它的好处是,我可以将"标头"复制粘贴到我需要在其中运行的任何批处理脚本中。由于不需要它,所以我没有费心使其递归安全,因此请确保您在放入之前先测试该方案。
显然,我在这些debug *调用中具有包装器功能,因此我不会在每次调用时都随身携带日志文件。另外,在调试日志调用中,我还测试了调试标志,因此包装器本身具有更多的逻辑,这主要取决于所使用的脚本。
显而易见的解决方法是将调用的输出重定向到临时文件,将其用作find / tee的输入,然后删除文件:
1 2 3 | @call :Label-02 second > tmp tee call-test.log < tmp delete tmp |
这是jebs答案的更简洁版本。
它使用相同的goto技术,但不是在重新输入时传递唯一的" START"参数,而是使用子字符串提取测试第一个参数的第一个字符是否为":",并且仅在有标签的情况下才调用goto。这简化了调用,但是您不能将子字符串提取与%1变量或空/不存在的变量一起使用,因此必须使用始终包含值的临时变量。无论如何,它都需要temp var来记住标签,因为
[更新:必须执行
1 2 | set"LABEL=%~1_" if"%LABEL:~0,1%"==":" SHIFT /1 & goto %LABEL:~0,-1% |
因此,以下脚本显示了如何使用传递给原始脚本的参数以及如何重新输入以处理标签:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @echo off set"LABEL=%~1_" if"%LABEL:~0,1%"==":" SHIFT /1 & goto %LABEL:~0,-1% call"%~f0" :LABEL_TEST param1 p2 | findstr foo echo param 1 is %1 exit /b :LABEL_TEST echo (foo) called label with PARAMS: %1 %2 %3 echo (bar) called label with PARAMS: %1 %2 %3 exit /b |
将输出:
1 2 3 | C:\>call-test-with-params TEST (foo) called label with PARAMS: param1 p2 param 1 is TEST |
问题的解决方案:
该脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | @echo off set"LABEL=%~1_" if"%LABEL:~0,1%"==":" SHIFT /1 & goto %LABEL:~0,-1% @rem call-test.cmd @rem _________________________________________________ @rem Test :label call option for .cmd files. @rem @echo ^ [start] @call"%~f0" :Label-03 p1 @call"%~f0" :Label-02 second | find"" @call"%~f0" :Label-02 second | tee call-test.log @goto Done @rem _________________________________________________ :Label-01 @echo ^ label 01 :: %1 @goto Exit @rem _________________________________________________ :Label-02 @echo ^ label 02 :: %1 @goto Exit @rem _________________________________________________ :Label-03 @echo ^ label 03 :: %1 @goto Exit @rem _________________________________________________ :Done @echo ^ [done] @pause @rem _________________________________________________ :Exit @exit /b |
将回显:
1 2 3 4 5 6 7 | C:\>call-test [start] label 03 :: p1 label 02 :: second label 02 :: second [done] Press any key to continue . . . |
并且
1 2 | C:\>more call-test.log label 02 :: second |
我认为您可以使用" |"然后将管道视为常规字符。
将