web-dev-qa-db-fra.com

Comment utiliser la fonctionnalité de tuyau CMD de Windows (|) avec l'option de commande CALL: Label?

J'ai un problème frustrant lorsque je veux utiliser la fonctionnalité pipe (|) avec l'option CALL: Label du shell CMD de la fenêtre. J'ai un très petit exemple (ci-dessous): call-test .cmd et exemple de sortie.

Le nœud du problème était/consiste à diriger la sortie d'un script CMD vers un autre programme, par exemple l'utilitaire tee ou la commande find. Par exemple:

    @call   :Label-02  param  | tee call-test.log

Ce qui lancerait le fichier de commande actuel à l’étiquette Label-02 et dirigerait la sortie vers tee. Malheureusement, l'utilisation du caractère de canal (|) sur la ligne avec l'option "call: label" donne une erreur:

Invalid attempt to call batch label outside of batch script.

Alors que "call example.cmd | tee example.log" fonctionne très bien.

L'autre redirection IO > fonctionne correctement. C'est le seul cas où "call: label pipe" (|) "est utilisé et qui échoue. Pour moi, cela ressemble à un bug de Windows.

Est-ce que quelqu'un a une solution de contournement et/ou est au courant d'une explication? 

Merci, Volonté


  • sortie de test d'appel

    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 . . .
    
  • call-test

    @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
    
16
will

La raison en est qu'un tuyau commence les deux côtés dans un contexte cmd (les deux sont parallèles dans une boîte à cmd), et que chaque côté est interprété comme un véritable argument de ligne de commande et que les étiquettes de ligne de commande ne sont pas autorisées.

Mais vous pouvez appeler votre fonction si vous redémarrez votre lot.

if not "%1"=="" goto %1
@call "%~0" :Label-02  param  | tee call-test.log

EDIT: L'échantillon complet

@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
13
jeb

La solution évidente consiste à rediriger la sortie de l'appel vers un fichier temporaire, à l'utiliser comme entrée pour find/tee, puis à supprimer le fichier:

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

Je réalise que cela arrive un peu tard, mais pourrait être utile pour les autres. ce n'est pas tout à fait un bidouillage, plus une solution de contournement. Ou joli "Nice hack" si vous devez .. .. J'utilise la solution suivante à un problème similaire:

@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

La bonne chose à ce sujet est que je peux copier-coller "l'en-tête" dans n'importe quel script batch dont j'ai besoin pour l'exécuter. Je ne me suis pas soucié de le rendre sûr, car je n'en avais pas besoin, alors Testez ce scénario avant de le mettre dans . De toute évidence, j'ai des fonctions d'emballage sur ces appels de débogage *, de sorte que je ne porte pas le fichier journal avec chaque appel. De plus, dans mes appels au journal de débogage, je teste également l'indicateur de débogage, de sorte que le wrapper lui-même a plus de logique, ce qui dépend principalement du script utilisé dans.

1
ciuly

Ceci est une version plus succincte de la réponse jebs.

Il utilise la même technique goto, mais au lieu de passer un paramètre "START" unique lors de la nouvelle saisie, il teste si le premier caractère du premier paramètre est ":" à l'aide d'une extraction de sous-chaîne et appelle uniquement goto si c'est le cas. une marque. Cela simplifie l'appel, toutefois, vous ne pouvez pas utiliser l'extraction de sous-chaîne avec des variables% 1 ou des variables vides/inexistantes. Vous devez donc utiliser une variable temporaire qui contient toujours une valeur. Quoi qu'il en soit, il a besoin de la variable temp pour mémoriser l'étiquette car SHIFT /1 supprime le premier paramètre: LABEL, mais il ne doit utiliser qu'une fois SHIFT et ne nécessite pas de paramètre supplémentaire sur le site de l'appel.

[update: doit faire shift /1 pour éviter de changer% 0 au cas où il serait utilisé par le script]

set "LABEL=%~1_"
if "%LABEL:~0,1%"==":" SHIFT /1 & goto %LABEL:~0,-1%

Ainsi, le script suivant montre comment utiliser les paramètres transmis au script d'origine, ainsi que pour le ressaisir afin de traiter les étiquettes:

@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

affichera:

C:\>call-test-with-params TEST
(foo) called label with PARAMS: param1 p2
param 1 is TEST

la ligne echo (bar) étant arrachée par le tuyau pour findstr


solution à la question:

Ce script:

@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

va faire écho:

C:\>call-test
    [start]
    label 03 :: p1
    label 02 :: second
    label 02 :: second
    [done]
Press any key to continue . . .

Et call-test.log a le contenu correct:

C:\>more call-test.log
    label 02 :: second
0
Sam Hasler

Je pense que vous pouvez utiliser "|" alors la pipe est traitée comme un caractère ordinaire.

0
Ravi Sambangi