web-dev-qa-db-ja.com

色とりどりのグレップ

各grepコマンドを実行して、結果を異なる色で強調表示しようとしています。次のような行で手動で行うことができます。

ls -l GREP_COLORS='mt=01;32' grep c | GREP_COLORS='mt=01;31' grep o | GREP_COLORS='mt=01;34' grep n | GREP_COLORS='mt=01;36' grep f

すべてのc文字は緑色で強調表示され、すべてのo文字は赤色で強調表示されます、など...

この例が機能するためには、grepコマンドに常に--color=alwaysがあることを確認する必要があります。これを.bashrcに設定したので、grepは常に色を持ちます:

export GREP_OPTIONS='--color=always'


私が達成しようとしているのは、この機能をエイリアスでラップすることです。そのため、grepを呼び出して、毎回異なるGREP_COLORS値を持つことができます。新しいパイプで接続されたgrepごとに複数のシェルの考慮事項を理解し、すでに使用されていることを示すためにいくつかのファイル(色ごとに1つ)を作成することでこれを克服しようとしています。

私はいくつかの試みをしましたが、奇妙なことに、これは「最高」で動作するようです。これは.bashrcにあります:

alias mg="mygrep"
mygrep(){
    # define possible colors
    COLORS=("01;32" "01;31" "01;34" "01;36")
    COUNTER=0
    NUM=0
    # as long as the color has already been used, keep searching
    while [ -f /home/lior/Desktop/mygrep_$NUM ]; do
        # get a random index
        let NUM=`shuf --input-range=0-$(( ${#COLORS[*]} - 1 )) | head -1`
        wait ${!}
        $(( COUNTER+=1 ))
        if [ "$COUNTER" -ge ${#COLORS[@]} ]; then
            # remove all color locks
            rm /home/lior/Desktop/mygrep_*
            wait ${!}
        fi
    done
    # mark this color as used
    touch /home/lior/Desktop/mygrep_$NUM
    wait ${!}

    # lets go!
    GREP_COLORS="mt=${COLORS[$NUM]}" grep "$@"
}

私はこのエイリアスを次のように使用しています:

ll | mg c | mg o | mg n | mg f

結果はかなりクールです。ただし、毎回若干異なるエラーがいくつかあります。ここにいくつかのスクリーンショットがあります:

シェルが各パイプコマンドを実行するときに、前の関数はまだ実行を完了していません。存在しないファイルを削除しようとします。他のcommand not foundエラーがどこから発生しているかはわかりません。

ご覧のとおり、ファイル操作を完了させるためにいくつかのwaitコマンドを入れましたが、これはあまりうまく機能していないようです。もう1つ試したことは、共有メモリ/dev/shmを使用することですが、同じような結果が得られました。

希望する結果を得るにはどうすればよいですか?

注意:

使用したい機能がたくさんあり、パイプの間に他のロジックを挿入するつもりなので、grepコマンドを単純にラップする答えを探しています。したがって、すべての検索語を一度に提供したくありません。他の「grepのような」ツールも探していません。申し訳ありません @ terdon 素晴らしいPerlの提案をすでに投稿している人。

30
Lix

ここに別のアプローチがあります。別の回答で すでに投稿されています の小さなPerlスクリプトがあり、ユーザーが指定したパターンを異なる色で強調表示します。スクリプトのわずかに変更されたバージョンは、grepのように動作します。

#!/usr/bin/env Perl
use Getopt::Std;
use strict;
use Term::ANSIColor; 

my %opts;
getopts('hic:l:',\%opts);
    if ($opts{h}){
      print<<EoF; 
Use -l to specify the pattern(s) to highlight. To specify more than one 
pattern use commas. 

-l : A Perl regular expression to be colored. Multiple expressions can be
     passed as comma separated values: -l foo,bar,baz
-i : makes the search case sensitive
-c : comma separated list of colors;

EoF
      exit(0);
    }

my $case_sensitive=$opts{i}||undef;
my @color=('bold red','bold blue', 'bold yellow', 'bold green', 
       'bold Magenta', 'bold cyan', 'yellow on_blue', 
       'bright_white on_yellow', 'bright_yellow on_red', 'white on_black');
if ($opts{c}) {
   @color=split(/,/,$opts{c});
}
my @patterns;
if($opts{l}){
     @patterns=split(/,/,$opts{l});
}
else{
    $patterns[0]='\*';
}

# Setting $| to non-zero forces a flush right away and after 
# every write or print on the currently selected output channel. 
$|=1;

while (my $line=<>) 
{ 
    my $want=0;
    for (my $c=0; $c<=$#patterns; $c++){
    if($case_sensitive){
        if($line=~/$patterns[$c]/){
           $line=~s/($patterns[$c])/color("$color[$c]").$1.color("reset")/ge;
           $want++;
        }
    }
    else{
        if($line=~/$patterns[$c]/i){
          $line=~s/($patterns[$c])/color("$color[$c]").$1.color("reset")/ige;
          $want++;
        }
      }
    }
print STDOUT $line if $want>0;
}

そのスクリプトをcgrepとしてPATHのどこかに保存し、実行可能にすると、最大10の異なるパターンを指定でき、それぞれ異なる色で印刷されます。

enter image description here

$ cgrep -h
Use -l to specify the pattern(s) to highlight. To specify more than one 
pattern use commas. 

-l : A Perl regular expression to be colored. Multiple expressions can be
     passed as comma separated values: -l foo,bar,baz
-i : makes the search case sensitive
-c : comma separated list of colors;
6
terdon

パイプでのgrepの呼び出しはそれぞれ別のシェルで実行されるため、それらの間で状態を渡す必要があります。次の解決策は、カラーインデックスを保持するファイルと、同時呼び出しが同じ値を読み取らないようにするロックファイルでそれを処理する大まかな方法​​です。

#!/usr/bin/env bash
color_index_file=~/.gitcolor
color_index_lock_file=/tmp/$(basename $0)

colors=()
for index in {31..34}
do
    colors+=("01;$index")
done

until mkdir "$color_index_lock_file" 2>/dev/null
do
    :
done

color_index=$(($(cat "$color_index_file" || echo 0) + 1))

if [[ $color_index -ge ${#colors[@]} ]]
then
    color_index=0
fi

printf "$color_index" > "$color_index_file"
rmdir "$color_index_lock_file"

GREP_COLORS="mt=01;${colors[$color_index]}" grep --color=always "$@"

コピーにcgrepという名前を付けて、PATHに配置したと想定してテストします。

echo foobarbaz | cgrep foo | cgrep bar | cgrep baz
5
l0b0

正規表現に慣れている場合は、grcとgrcatを調べてみてください。 grcはgrcatを呼び出します。

grcatは、各色で表示されるテキストに一致する正規表現を追加できる構成ファイルを使用します。他にも多くのオプションがあります。デフォルトでは、システムログファイルが色分けされます。

最終的なスクリプトで何を念頭に置いているかによって、1つのコマンドだけで出力を色分けできる場合があります。

トリックは、データソースの各「フィールド」の正規表現を正しく指定することです。データの構造が比較的均一であれば、これははるかに簡単です。

前回試してみたが、それほど遠くまでは行かなかったが、正規表現の方が以前よりも少し上手なので、もう一回試してみるかもしれない。

ターミナルデバイスに直接情報(色の変更など)を送信するために使用できるtputコマンドもあります。

どちらのアプローチについても、以下の投稿で取り上げます。これは、findコマンドについて説明しますが、任意のコマンドの出力に適用できます。

カラーFIND出力?

1
Joe