=encoding utf-8

=head1 NAME

App::Greple::tee - マッチしたテキストを外部コマンドの結果に置き換えるモジュール

=head1 SYNOPSIS

    greple -Mtee command -- ...

=head1 VERSION

Version 1.02

=head1 DESCRIPTION

Greple の B<-Mtee> モジュールは、マッチしたテキスト部分を指定されたフィルタコマンドに送り、その結果で置き換えます。このアイデアは、B<teip>というコマンドから派生したものです。これは、外部のフィルタコマンドに部分的なデータをバイパスするようなものである。

Filterコマンドはモジュール宣言(C<-Mtee>)に続き、2つのダッシュ(C<-->)で終了する。例えば、次のコマンドは、データ中の一致した単語に対して、C<a-z A-Z> の引数を持つコマンド C<tr> コマンドを呼び出します。

    greple -Mtee tr a-z A-Z -- '\w+' ...

上記のコマンドは、マッチした単語をすべて小文字から大文字に変換する。B<greple>はB<--cm>オプションでより効果的に同じことができるので、実はこの例自体はあまり意味がない。

デフォルトでは、コマンドは1つのプロセスとして実行され、マッチしたデー タはすべて混在してプロセスに送られる。マッチしたテキストが改行で終わっていない場合は、送信前に追加され、受信後に削除される。入力データと出力データは行ごとにマップされるので、入力と出力の行数は同じでなければならない。

B<--discrete>オプションを使うと、マッチしたテキスト・エリアごとにコマンドが呼び出されます。この違いは以下のコマンドでわかる。

    greple -Mtee cat -n -- copyright LICENSE
    greple -Mtee cat -n -- copyright LICENSE --discrete

B<--discrete>オプションを使用する場合、入出力データの行数は同一である必要はありません。

=head1 OPTIONS

=over 7

=item B<--discrete>

一致した部品に対して、個別に新しいコマンドを起動する。

=item B<--bulkmode>

<--discrete>オプションをつけると、各コマンドはオンデマンドで実行される。このオプションは
<--bulkmode> option causes all conversions to be performed at once.

=item B<--crmode>

このオプションは、各ブロックの途中にある改行文字をすべてキャリッジリターン文字に置き換える。コマンドの実行結果に含まれるキャリッジリターンは改行文字に戻される。このように、複数行からなるブロックは、B<--discrete>オプションを使わずに一括して処理することができる。

=item B<--fillup>

一連の空白でない行を1行にまとめてからフィルターコマンドに渡す。幅の広い文字の間の改行文字は削除され、その他の改行文字はスペースに置き換えられる。

=item B<--squeeze>

2つ以上の連続する改行文字を1つにまとめる。

=item B<-ML> B<--offload> I<command>

L<teip(1)> の B<--offload> オプションは、別のモジュール L<App::Greple::L> (B<-ML>) に実装されています。

    greple -Mtee cat -n -- -ML --offload 'seq 10 20'

B<-ML>モジュールを使って、次のように偶数行だけを処理することもできます。

    greple -Mtee cat -n -- -ML 2::2

=back

=head1 LEGACIES

B<--blocks>オプションは、B<--stretch> (B<-S>)オプションがB<greple>に実装されたので、必要なくなりました。単純に以下のように実行すればよい。

    greple -Mtee cat -n -- --all -SE foo

B<--blocks>は将来廃止される可能性があるので、使用することは推奨されない。


=over 7

=item B<--blocks>

通常、指定された検索パターンにマッチする領域が外部コマンドに送られる。このオプションが指定されると、マッチした領域ではなく、それを含むブロック全体が処理される。

例えば、パターンC<foo>を含む行を外部コマンドに送るには、行全体にマッチするパターンを指定する必要がある：

    greple -Mtee cat -n -- '^.*foo.*\n' --all

しかし、B<--blocks> オプションを使えば、次のように簡単にできる：

    greple -Mtee cat -n -- foo --blocks

B<--blocks> オプションをつけると、このモジュールは L<teip(1)> の B<-g> オプションに似た挙動をする。B<--blocks> オプションを使うと、このモジュールはより L<teip(1)> の B<-g> オプションに近い動作をする。

B<--blocks> を B<--all> オプションと一緒に使わないでください。

=back

=head1 WHY DO NOT USE TEIP

まず第一に、B<teip>コマンドでできることは、いつでもそれを使ってください。これは優れたツールで、B<greple>よりずっと速いです。

B<greple>は文書ファイルの処理を目的としているため、マッチエリアの制御など、それに適した機能を多く持っています。それらの機能を活用するために、B<greple>を使う価値はあるかもしれません。

また、B<teip>は複数行のデータを1つの単位として扱うことができませんが、B<greple>は複数行からなるデータチャンクに対して個別のコマンドを実行することが可能です。

=head1 EXAMPLE

次のコマンドは，Perlモジュールファイルに含まれるL<perlpod(1)>スタイルドキュメント内のテキストブロックを検索します。

    greple --inside '^=(?s:.*?)(^=cut|\z)' --re '^([\w\pP].+\n)+' tee.pm

このようにB<Mtee>モジュールと組み合わせてB<deepl>コマンドを呼び出すと、DeepLサービスによって翻訳することができます。

    greple -Mtee deepl text --to JA - -- --fillup ...

ただし、この場合は専用モジュール L<App::Greple::xlate::deepl> の方が効果的です。実は、B<tee>モジュールの実装のヒントはB<xlate>モジュールからきています。

=head1 EXAMPLE 2

次に、LICENSE文書にインデントされた部分があります。

    greple --re '^[ ]{2}[a-z][)] .+\n([ ]{5}.+\n)*' -C LICENSE

      a) distribute a Standard Version of the executables and library files,
         together with instructions (in the manual page or equivalent) on where to
         get the Standard Version.

      b) accompany the distribution with the machine-readable source of the Package
         with your modifications.

この部分はB<tee>モジュールとB<ansifold>コマンドで整形することができます。

    greple -Mtee ansifold -rsw40 --prefix '     ' -- --discrete --re ...

      a) distribute a Standard Version of
         the executables and library files,
         together with instructions (in the
         manual page or equivalent) on where
         to get the Standard Version.

      b) accompany the distribution with the
         machine-readable source of the
         Package with your modifications.

discreteオプションは複数のプロセスを起動するので、プロセスの実行に時間がかかる。そのため、C<--separate ' \r'>オプションとC<ansifold>を併用すると、NLの代わりにCR文字を使って1行にすることができます。

    greple -Mtee ansifold -rsw40 --prefix '     ' --separate '\r' --

その後、L<tr(1)>コマンドなどでCRをNLに変換する。

    ... | tr '\r' '\n'

=head1 EXAMPLE 3

ヘッダ行以外から文字列を grep したい場合を考えてみましょう。例えば、C<docker image ls>コマンドからDockerイメージ名を検索したいが、ヘッダ行は残したいとします。以下のコマンドで可能です。

    greple -Mtee grep perl -- -ML 2: --discrete --all

オプション C<-ML 2:> は最後から2番目の行を取り出し、C<grep perl> コマンドに送ります。入出力の行数が変わるので --discrete オプションが必要だが、コマンドは一度しか実行されないので、性能上の欠点はない。

B<teip>コマンドで同じことをしようとすると、C<teip -l 2- -- grep>は出力行数が入力行数より少ないのでエラーになる。しかし、得られる結果に問題はない。

=head1 INSTALL

=head2 CPANMINUS

    $ cpanm App::Greple::tee

=head1 SEE ALSO

L<App::Greple::tee>, L<https://github.com/kaz-utashiro/App-Greple-tee>

L<https://github.com/greymd/teip>

L<App::Greple>, L<https://github.com/kaz-utashiro/greple>

L<https://github.com/tecolicom/Greple>

L<App::Greple::xlate>

=head1 BUGS

C<--fillup> オプションは、韓国語テキストを連結する際に、ハングル文字間の空白を削除する。

=head1 AUTHOR

Kazumasa Utashiro

=head1 LICENSE

Copyright © 2023-2025 Kazumasa Utashiro.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
