WindowのCMDシェルのCALL:Labelオプションでpipe()機能を使用したいのですが、イライラする問題があります。私は非常に小さな例(以下)を持っています:call-test。cmdとサンプル出力。
問題の要点は、CMDスクリプトの出力を別のプログラム、たとえばteeユーティリティ、またはfindコマンドにパイプすることでした。例えば:
@call :Label-02 param | tee call-test.log
これは、現在のコマンドファイルをラベルLabel-02で開始し、出力をteeにパイプします。残念ながら、「call:label」オプションを含む行でパイプ文字(|)を使用すると、エラーが発生します。
Invalid attempt to call batch label outside of batch script.
一方、「call example.cmd | tee example.log」は問題なく機能します。
もう1つのIO redirection >は問題なく動作します。 "call:label pipe(|)"を使用するのは、私にとっては、Windowsのバグのように見えます。
誰かが回避策を持っているか、および/または説明を知っていますか?
ありがとう、ウィル
コールテスト出力
c:\> 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 . . .
コールテスト
@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コンテキストで両方を開始し(両方が1つのcmd-boxで並列に実行される)、両側が実際のコマンドライン引数として解釈され、cmdラインではラベルが許可されないためです。
ただし、バッチを再起動すると、関数を呼び出すことができます。
if not "%1"=="" goto %1
@call "%~0" :Label-02 param | tee call-test.log
編集:完全なサンプル
@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
明らかな回避策は、呼び出しの出力を一時ファイルにリダイレクトし、それをfind/teeの入力として使用してから、ファイルを削除することです。
@call :Label-02 second > tmp
tee call-test.log < tmp
delete tmp
これは少し遅くなると思いますが、他の人に役立つかもしれません。これはかなりハックではなく、回避策です。または、必要な場合はかなり「素敵なハック」。私は同様の問題に次の解決策を使用しています:
@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 *呼び出しにはラッパー関数があるので、呼び出しごとにログファイルを持ち歩かなくて済みます。また、デバッグログの呼び出しでは、デバッグフラグもテストしているため、ラッパー自体にロジックが追加されています。これは、主に使用されるスクリプトに依存します。
これは、Jebs Answerのより簡潔なバージョンです。
同じgotoテクニックを使用しますが、再入力時に一意の「START」パラメーターを渡す代わりに、最初のパラメーターの最初の文字が「:」であるかどうかをテストします サブストリング抽出 を使用して呼び出しのみラベルならgoto。これにより呼び出しが簡略化されますが、%1変数または空/存在しない変数で部分文字列抽出を使用できないため、常に値を含む一時変数を使用する必要があります。 _SHIFT /1
_は最初の:LABELパラメータを削除するため、とにかくtemp varでラベルを覚える必要がありますが、使用する必要があるのは [〜#〜] shift [〜#〜] を1回だけです。呼び出しサイトで追加のパラメーターを必要としません。
[update:スクリプトで使用される場合に%0が変更されないようにするには、_shift /1
_を実行する必要があります]
_set "LABEL=%~1_"
if "%LABEL:~0,1%"==":" SHIFT /1 & goto %LABEL:~0,-1%
_
したがって、次のスクリプトは、元のスクリプトに渡されたパラメーターを使用して、ラベルを処理するために再入力する方法を示しています。
_@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
_
出力されます:
_C:\>call-test-with-params TEST
(foo) called label with PARAMS: param1 p2
param 1 is TEST
_
findstrへのパイプによって取り除かれるecho (bar)
行
このスクリプト:
_@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
_
エコーします:
_C:\>call-test
[start]
label 03 :: p1
label 02 :: second
label 02 :: second
[done]
Press any key to continue . . .
_
そして_call-test.log
_は正しい内容を持っています:
_C:\>more call-test.log
label 02 :: second
_
「|」を使用できると思いますパイプは通常の文字として扱われます。