Как использовать функцию Windows CMD pipe (|) с опцией команды CALL: Label? - PullRequest
18 голосов
/ 21 ноября 2010

У меня неприятная проблема, когда я хочу использовать функцию конвейера ( | ) с опцией CMD оболочки Windows CALL: Label . У меня есть очень маленький пример (ниже): call-test .cmd и пример вывода.

Суть проблемы заключалась в том, чтобы / было направить вывод сценария CMD в другую программу, например, утилиту tee или команду find . Например:

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

Который запустит текущий командный файл с меткой Метка-02 и перенаправит вывод в tee . К сожалению, использование символа канала (|) в строке с опцией «call: label» выдает ошибку:

Invalid attempt to call batch label outside of batch script.

Принимая во внимание, что " call example.cmd | tee example.log" работает просто отлично.

Другое перенаправление ввода / вывода > работает нормально. Это только один случай, когда используется " 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
    

Ответы [ 6 ]

14 голосов
/ 21 ноября 2010

Причина в том, что канал запускает обе стороны в контексте cmd (обе они выполняются параллельно в одном cmd-поле), и каждая сторона интерпретируется как реальный аргумент командной строки, а метки строки 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
1 голос
/ 24 января 2012

Я понимаю, что это немного поздно, но может быть полезно для других. это не совсем взлом, скорее обходной путь. Или довольно "хороший хак", если нужно. Я использую следующее решение похожей проблемы:

@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 голос
/ 21 ноября 2010

Очевидный обходной путь - перенаправить вывод вызова во временный файл, использовать его как вход для find / tee, а затем удалить файл:

@call :Label-02 second > tmp
tee call-test.log < tmp
delete tmp
0 голосов
/ 05 февраля 2016

Это более лаконичная версия ответа Джебса.

Он использует ту же технику goto, но вместо передачи уникального параметра "START" при повторном вводе он проверяет, является ли первый символ первогоПараметр ":" использует извлечение подстроки и вызывает goto только если это метка.Это упрощает вызов, однако вы не можете использовать извлечение подстроки с переменными% 1 или пустыми / несуществующими переменными, поэтому необходимо использовать временную переменную, которая всегда содержит значение.В любом случае ему требуется временная переменная, чтобы запомнить метку, так как SHIFT /1 удалит параметр first: LABEL, но он должен использовать SHIFT только один раз и не требует дополнительного параметра на сайте вызова.

[ update: должен выполнить shift /1, чтобы избежать изменения% 0 в случае, если он используется сценарием]

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

строка 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 голосов
/ 25 октября 2013

Я думаю, что вы можете использовать "|"тогда труба обрабатывается как обычный символ.

0 голосов
/ 30 декабря 2010

поставьте ^ перед командой pipe .... например,

@call :Label-02 second ^| find " "

...