web-dev-qa-db-ja.com

CALL:LabelコマンドオプションでWindows CMDパイプ(|)機能を使用する方法

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
    
18
will

原因は、パイプが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
14
jeb

明らかな回避策は、呼び出しの出力を一時ファイルにリダイレクトし、それをfind/teeの入力として使用してから、ファイルを削除することです。

@call :Label-02 second > tmp
tee call-test.log < tmp
delete tmp
1
chalup

これは少し遅くなると思いますが、他の人に役立つかもしれません。これはかなりハックではなく、回避策です。または、必要な場合はかなり「素敵なハック」。私は同様の問題に次の解決策を使用しています:

@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 *呼び出しにはラッパー関数があるので、呼び出しごとにログファイルを持ち歩かなくて済みます。また、デバッグログの呼び出しでは、デバッグフラグもテストしているため、ラッパー自体にロジックが追加されています。これは、主に使用されるスクリプトに依存します。

1
ciuly

これは、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
_
0
Sam Hasler

「|」を使用できると思いますパイプは通常の文字として扱われます。

0
Ravi Sambangi