1 (изменено: Rumata, 2022-05-10 02:23:51)

Тема: CMD/BAT: дублирование вывода команды (обсуждение)

Коллега Poltergeyst в теме CMD/BAT: дублирование вывода команды поделился своим вариантом реализации команды tee из мира unix. Тема открыта в разделе Коллекции, поэтому обсуждение здесь, отдельной темой.

Я тоже задавался вопросом "а не сделать ли и мне что-то подобное" на чистом батнике, но мне было лень. А еще я видел весьма интересные, но несколько запутанные и ограниченные решения. Например, здесь: Asynchronous native batch tee script. И там используется временный файл, что, на мой взгляд, не очень хорошо.

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


C:\>tee
Usage: tee [/a] [FILE]...

Copy STDIN to each file and also to STDOUT

/a  Append to the given files, don't overwrite

Преимущества данного подхода:
1. возможность писать сразу в несколько файлов
2. возможность дописывать в существующие файлы (ключ /A)

+ Исходный код (гибрид BAT+JS)

0</*! ::
::>Usage: tee [/a] [FILE]...
::>
::>Copy STDIN to each file and also to STDOUT
::>
::>/a  Append to the given files, don't overwrite
@echo off
timeout /t 0 >nul 2>&1 && (
	for /f "tokens=1,* delims=>" %%a in ( 'findstr "^::>" "%~f0"' ) do echo:%%b
	goto :EOF
)

cscript //nologo //e:javascript "%~f0" %*
goto :EOF
*/0;

// Declare synonyms and references to the global WSH objects
var stdin  = WScript.StdIn;
var stdout = WScript.StdOut;
var stderr = WScript.StdErr;

var nargs = WScript.Arguments.Named;
var uargs = WScript.Arguments.Unnamed;

var fso = new ActiveXObject('Scripting.FileSystemObject');

// Constants
var F_MODE_WRITE = 2;
var F_MODE_APPEND = 8;
var F_FORMAT_DEFAULT = -2;

// Append or overwrite
var append = nargs.Exists('A');

// STDOUT is one of the targets
var files = [ {
	filename: '<stdout>',
	handler: stdout
} ];

for (var i = 0; i < uargs.length; i++) {
	var e;
	try {
		var f = uargs.item(i);
		var h = fso.OpenTextFile(
			f,
			append ? F_MODE_APPEND : F_MODE_WRITE,
			true,
			F_FORMAT_DEFAULT);
		files.push({
			filename: f,
			handler: h
		});
	} catch(e) {
		// Don't stop execution, just report the problem and continue
		stderr.WriteLine([
			WScript.ScriptName,
			f,
			e.description
		].join(': '));
	}
}

// Read STDIN line by line and put each line to all available targets
while ( ! stdin.AtEndOfStream ) {
	var line = stdin.ReadLine();
	for (var i = 0; i < files.length; i++) {
		files[i].handler.WriteLine(line);
	}
}

// Let's close all files gracefully
for (var i = 1; i < files.length; i++) {
	files[i].handler.Close();
}
+ Лирическое отступление

Наконец-то задумался о названии программы. Оказывается оно возникло из-за сходства с тройниками (синонимы: фитинг, т-образный переходник или т-образный разветвитель) и их функционалом (один поток развести на два направления). В английском их называют t-splitter и произносят, соответственно, [tee]. Отсюда и название программы.

( 2 * b ) || ! ( 2 * b )

2

Re: CMD/BAT: дублирование вывода команды (обсуждение)

Подобного рода "велосипеды" актуальны разве что в старых версиях Windows, да и там они без особой надобности, если:
1) установлен Cygwin или MSYS
2) из исходников собраны GNU утилиты
3) в качестве текстового процессора используется Vim
и т.д.
В остальном можно пойти по пути наименьшего сопротивления:

dir X:\path > log.txt & type log.txt

Это конечно не "расщепление" потока, но как вариант, если не используется PowerShell, где tee уже имеется (почему люди по-прежнему насилуют cmd, остаётся загадкой). Что же до поздних ОС, с приходом WSL необходимость в кулибинском подходе отпадает:

wsl -e bash -c "ls -g /mnt/x/path | tee log.txt"

3

Re: CMD/BAT: дублирование вывода команды (обсуждение)

greg zakharov пишет:

почему люди по-прежнему насилуют cmd

Во-первых, очень интересно реализовать некий функционал имея весьма ограниченные средства. Во-вторых, многие писатели "велосипедов" уже имеют у себя гораздо продвинутые средства (те же Cygwin, MSYS). Тем не менее они по каким-то причинам пишут "велосипеды".

WSL - это хорошо, но, по умолчанию, он не работает, и его надо доустанавливать (это не аргумент, ведь мы и сами качаем кучу программ для своего удобства). PowerShell крутой и значительно продвинутый по сравнению с CMD. Но не всем он по вкусу. А еще говорят, что не на всех системах он установлен. Не уверен, что достоверно.

Ну и наконец, это весело (см. "во-первых" выше).

greg zakharov пишет:

dir X:\path > log.txt & type log.txt

Простите, но Ваш пример не выдеривает критики. Смысл команды tee заключается в синхронной записи логов в файл и консоль.

( 2 * b ) || ! ( 2 * b )

4

Re: CMD/BAT: дублирование вывода команды (обсуждение)

Rumata пишет:

Простите, но Ваш пример не выдеривает критики.

Так и быть, прощаю, но в виду вашей невнимательности. Ибо як глаголилось русской гравою - то не распоточивание, що лишь "имитация".

Rumata пишет:

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

Отнюдь. Как балакают в народе: кому что, а лысому - расчёска. Может кому-то и впрямь приятен весь этот БДСМ с cmd, но за время потраченное на "некрофилию" можно вполне освоить что-то в значительной мере упрощающее жизнь, нервы, а самое главное - экономящее время. Только не подумайте, что это какая-то пропаганда, скорее - здравый смысл.

Rumata пишет:

PowerShell крутой и значительно продвинутый по сравнению с CMD. Но не всем он по вкусу.

Те же яйца (точнее расчёска), только в профиль.