
gnu sedはどのようにbash関数を実行してカウンタ値を増やすのですか?
$ k(){ ((i++)); echo $i ;} ;export -f k
$ i=; echo -e 'oAo\nooAo\nAo' | sed -E '/A/e k'
1
oAo
1
ooAo
1
Ao
カウンタを正しく実行する代わりに失敗します。
答え1
プロセスは親プロセスの環境に影響を与えることはできません。 sedのコマンドを使用すると、新しい e
シェルをフォークしてコマンドを実行し(エクスポートされたk
関数を継承します)、そのシェル変数に対するすべての変更はi
終了時に失われます(つまり、k関数はhourを終了します)。
変数を増やすには、環境外のどこかに変数を保存する必要があります。たとえば、ファイルから。ファイルを使用したくない場合は、SQLデータベースなどを使用できない理由はmemcached
ありませんredis
。
例えば:
k() {
local counterfile i
counterfile='/tmp/counter.i'
[ -e "$counterfile" ] && i=$(cat "$counterfile")
((i++))
echo "$i" | tee "$counterfile"
}
export -f k
今実行するとカウンタが増えます。
$ rm /tmp/counter.i
$ printf 'oAo\nooAo\nAo' | sed -E '/A/e k'
1
oAo
2
ooAo
3
Ao
$ echo 100 > /tmp/counter.i
$ printf 'oAo\nooAo\nAo' | sed -E '/A/e k'
101
oAo
102
ooAo
103
Ao
ところで、sedのe
コマンドは役に立ちますが、私の考えにはPerlがあなたがしている作業にもっと良い言語だと思います。例えば
$ printf 'oAo\nooAo\nAo' |
perl -lpe 'BEGIN { $i=shift || 0 };
if (/A/) {print ++$i}' 10
11
oAo
12
ooAo
13
Ao
引数を指定しない場合、$i
デフォルトは0です。
スクリプトが「sed」のように見えるようにするには、埋め込み行$i
にカウンタ変数をインクリメントして印刷する方法がありますA
。ここに別のものがあります:
/A/ && print ++$i
1行に複数の一致がある可能性があり、各一致に対してカウンタを増やして印刷する必要がある場合は、一致を繰り返す必要があります。例えば
$ printf 'oAo\noAoAoAo\nAo' |
perl -lpe 'BEGIN { $i=shift || 0 };
for (/oA/g) {print ++$i}'
1
oAo
2
3
4
oAoAoAo
Ao
または累積合計は必要ありませんが、各行の一致数を数える場合:
$ printf 'oAo\noAoAoAo\nAo' |
perl -lne '$c = () = $_ =~ /oA/g;
printf "%02i:%s\n", $c, $_'
01:oAo
03:oAoAoAo
00:Ao
この最後の項目には説明が必要な場合があります。読む戻る、この$c = () = $_ =~ /oA/g
ステートメントは最初に正規表現一致を実行し/oA/g
、リストコンテキストの結果を空のリストとして返し、変数に()
割り当てます$c
。配列/リストではなくスカラー変数であるため、スカラー$c
コンテキストで評価されるため、そのリストの要素数を返します。これは、一致の数を計算するために使用される非常に一般的なPerl慣用語です。
注:Perlには、-p
sedと非常によく似た方法で実行するオプションがあります(つまり、入力を繰り返して文が印刷を妨げない限り、処理後に各行を印刷します)。 Perlには-n
次のように動作させるオプションがありますsed -n
(入力を繰り返して明示的に印刷するように指示するだけ)。
最後に、このバージョンのsedおよびbash関数は、一致する各入力行に対して、およびを分岐することbash
にcat
注目する価値があります。 Perlバージョンは何もフォークせず、入力だけを繰り返します。 bashで実行するために外部プログラムをフォークする必要がある多くの作業は、独自の組み込み構文(または数千のライブラリモジュールのいずれか)を使用してPerlを介して内部的に実行できることにも注目に値します。tee
/A/
答え2
GNU sedを使用すると、目的の結果を得るためにユーザー定義関数に頼る必要はありません。ここでは、増分数を格納するためにストレージスペースを使用します。
echo -e 'oAo\nooAoAoA\no' |
sed -En "/A/!d
p;:a
H;g
s/^(\n*).*/expr '\1' : '.*'/ep
g;s//\1/;x;s/^\n*//;s/A//;//ba
"
出力:
oAo
1
ooAoAoA
2
3
4