Вопрос по windows – Использование команды Tee для файла .bat

11

Я пытаюсь использоватьтройник код, написанный для файла bat, но у меня возникли проблемы с его реализацией в моем коде. Я не хочу использовать какие-либо сторонние установки для решения этой проблемы, так как я хочу, чтобы код работал, если я форматирую свой компьютер через год и хочу снова запустить программу.

У меня это настроено таким образом:

mycommand.exe | tee.bat -a output.txt

Я пытался с отдельным.bat файл и пробовал в том числе в качестве функции (предпочтительно) в оригинале.bat безрезультатно с:

myprogram.exe | call tee -a output.txt
echo.
echo.
echo.
SET /P restart="Do you want to run again? (1=yes, 2=no): "
if "%restart%"=="1" GOTO LoopStart


::--------------------------------------------------------
::-- Function section starts below here
::--------------------------------------------------------
:tee
:: Check Windows version
IF NOT "%OS%"=="Windows_NT" GOTO Syntax
| 
:: Keep variables local
SETLOCAL

:: Check command line arguments
SET Append=0
IF /I [%1]==[-a] (
    SET Append=1
    SHIFT
)
IF     [%1]==[] GOTO Syntax
IF NOT [%2]==[] GOTO Syntax

:: Test for invalid wildcards
SET Counter=0
FOR /F %%A IN ('DIR /A /B %1 2^>NUL') DO CALL :Count "%%~fA"
IF %Counter% GTR 1 (
    SET Counter=
    GOTO Syntax
)

:: A valid filename seems to have been specified
SET File=%1

:: Check if a directory with the specified name exists
DIR /AD %File% >NUL 2>NUL
IF NOT ERRORLEVEL 1 (
    SET File=
    GOTO Syntax
)

:: Specify /Y switch for Windows 2000 / XP COPY command
SET Y=
VER | FIND "Windows NT" > NUL
IF ERRORLEVEL 1 SET Y=/Y

:: Flush existing file or create new one if -a wasn't specified
IF %Append%==0 (COPY %Y% NUL %File% > NUL 2>&1)

:: Actual TEE
FOR /F "tokens=1* delims=]" %%A IN ('FIND /N /V ""') DO (
    >  CON    ECHO.%%B
    >> %File% ECHO.%%B
)

:: Done
ENDLOCAL
GOTO:EOF


:Count
SET /A Counter += 1
SET File=%1
GOTO:EOF


:Syntax
ECHO.
ECHO Tee.bat,  Version 2.11a for Windows NT 4 / 2000 / XP
ECHO Display text on screen and redirect it to a file simultaneously
ECHO.
IF NOT "%OS%"=="Windows_NT" ECHO Usage:  some_command  ¦  TEE.BAT  [ -a ]  filename
IF NOT "%OS%"=="Windows_NT" GOTO Skip
ECHO Usage:  some_command  ^|  TEE.BAT  [ -a ]  filename
:Skip
ECHO.
ECHO Where:  "some_command" is the command whose output should be redirected
ECHO         "filename"     is the file the output should be redirected to
ECHO         -a             appends the output of the command to the file,
ECHO                        rather than overwriting the file
ECHO.
ECHO Written by Rob van der Woude
ECHO http://www.robvanderwoude.com
ECHO Modified by Kees Couprie
ECHO http://kees.couprie.org
ECHO and Andrew Cameron

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

How can I get the Tee command to work properly with my .bat so I can split the output to both a file and the console.

Хм, это работает для меня. Вы уверены, чтоmycommand.exe выводит в stdout, а не в stderr? Чтобы также захватить последнее, вам нужноmycommand.exe 2>&1 | call tee ... Ingo Karkat

Ваш Ответ

1   ответ
20

чной из-за того, как работают каналы Windows - Windows создает экземпляры обеих сторон канала с помощью новых оболочек CMD. Увидетьhttps://stackoverflow.com/a/8194279/1012053 для получения дополнительной информации.

Эта версия пакетной футболки Rob van der Woude не может работать для вас, потому что она используетFOR /F чтобы прочитать результаты команды - команда должна быть выполнена до завершения, прежде чем будут прочитаны какие-либо строки. Это не сработает, если вам потребуется взаимодействие с пользователем во время выполнения команды. С этой версией tee вы можете просто перенаправить вывод в файл, а затемTYPE файл, когда закончите. Очевидно, не то, что вы хотите.

Есть чистые пакетные приемы, которые могут приблизить вас, но я думаю, что есть еще одна проблема, которую нельзя решить с помощью чистой партии. Ваш исполняемый файл может поместить подсказку в строку без ввода новой строки. Я считаю, что чистая нативная партия всегда читает целые строки (кроме случаев, когда в конце потока). Я не знаю о пакетном методе чтения символа за символом.

Slight correction - SET /P can read partial lines of piped input, but it has limitations that prevent it from being used for a robust batch tee solution: There is no way to know for sure when each line ends. It is limited to 1021 characters per "line". It strips control characters from the end of each "line". There is no way to tell when it has reached the end of the input stream.

Но есть простое решение - JScript или VBScript работает как чемпион и не требует каких-либо специальных установок. Вот гибридный JScript / пакетный скрипт, который должен работать для вас. JScript плохо написан с большим количеством возможностей для улучшения. Например, нет проверки ошибок.

Я сохраняю скрипт какtee.bat, Первый обязательный аргумент указывает имя файла для записи. По умолчанию файл перезаписывается, если он уже существует. Если задан второй аргумент (значение не имеет значения), вместо этого вывод добавляется в файл.

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment

::--- Batch section within JScript comment that calls the internal JScript ----
@echo off
cscript //E:JScript //nologo "%~f0" %*
exit /b

----- End of JScript comment, beginning of normal JScript  ------------------*/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var mode=2;
if (WScript.Arguments.Count()==2) {mode=8;}
var out = fso.OpenTextFile(WScript.Arguments(0),mode,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
  chr=WScript.StdIn.Read(1);
  WScript.StdOut.Write(chr);
  out.Write(chr);
}

Использование в значительной степени, как вы ожидаете.

command.exe | tee.bat output.txt 1

Последний 1 аргумент заставляет добавить режим. Это может быть любое значение, кроме 1

Вы можете поместить все в один пакетный скрипт, как вам кажется.

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment

::--- Batch section within JScript comment ----------------------------
@echo off

::This block of code handles the TEE by calling the internal JScript code
if "%~1"=="_TEE_" (
  cscript //E:JScript //nologo "%~f0" %2 %3
  exit /b
)

::The rest of your batch script goes here

::This pipes to TEE in append mode
mycommand.exe | "%~f0" _TEE_ output.txt 1

exit /b

----- End of JScript comment, beginning of normal JScript  ------------------*/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var mode=2;
if (WScript.Arguments.Count()==2) {mode=8;}
var out = fso.OpenTextFile(WScript.Arguments(0),mode,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
  chr=WScript.StdIn.Read(1);
  WScript.StdOut.Write(chr);
  out.Write(chr);
}

Update

Для тех, кто имеет академический интерес к пакетным сценариям, я разместил чистую нативную пакетную версиюАсинхронный нативный пакетный сценарий в DosTips. Но этот гибридный подход - мое предпочтительное решение для сценариев.

+1 к ответу и комментарию, потому что это невероятно хороший ответ ...!
@jeb - Покажи мне больше! Я заинтригован.
Это работает отлично! Большое спасибо, я бы проголосовал 5 раз, если смогу. Nick
+1, здорово, только частьI believe pure native batch always reads entire lines неправильно, я использую это в некоторыхmultithreading партии
c: \ bin \ tee.bat (12, 1) Ошибка времени выполнения Microsoft JScript: нижний индекс вне диапазона

Похожие вопросы