複数行のgensub

複数行のgensub

次のようなランダムな行がたくさんあるファイルがあります

aaa bbb
ccc ddd
eee mark: 98 fff
ggg ggg jjjj iii
jjj kkkk

上記の数字「98」に一致させるために、awk と gensub のみを使用したいと思います。 今のところ、以下のコードがありますが、gensub で「\n」を他の文字として扱う必要があるため、機能しないと思います。

cat file.txt | awk 'printf(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

上記のコードの出力を「98」のみにする必要があります。どうすればいいでしょうか?

編集

s または m 修飾子を使用しても、期待どおりに動作しません。これは、私の知る限り、「s」修飾子により、正規表現は . を \n を含む任意の文字として扱う必要があるためです。

答え1

awk入力を複数行の文字列として扱うと考えているようですが、そうではありません。ファイルに対してawkスクリプトを実行すると、スクリプトは適用されます。ファイルの各行に別々に。つまり、gensub1 行につき 1 回実行されます。実際に必要な操作を実行できますawkが、実際には最適なツールではありません。

私の知る限り、大きなファイルがあり、mark:空白の後に続く数字のみを印刷したいようです。そうであれば、以下の方法はすべて、 をいじくり回すよりも簡単ですgensub

  1. grepPerl互換正規表現(-P)での使用

    $ grep -oP 'mark:\s*\K\d+' file 
    98
    

    は、行の一致する部分のみを出力します。 は、-o「このポイントより前に一致するものはすべて無視する」ことを意味する PCRE 構造です。grep\K

  2. sed

    $ sed -n 's/.*mark:\s*\([0-9]\+\).*/\1/p' file
    98
    

    -n通常の出力を抑制します。p最後の は、sed置換が成功した場合にのみ出力を行います。正規表現自体は、後続の数字の文字列mark:と 0 個以上の空白文字をキャプチャし、行全体をキャプチャしたものに置き換えます。

  3. パール

    $ perl -ne 'print if s/.*mark:\s*(\d+).*/$1/' file
    98
    

    は、-nperl に入力ファイルを 1 行ずつ読み取り、 で指定されたスクリプトを適用するように指示します-e。スクリプトは、置換が成功した行を出力します。

本当に を使用したい場合はgensub、次のようにします。

$ awk '/mark:/{print gensub(/.*mark:\s*([0-9]+).*/,"\\1","g")}' file
98

個人的には、awk で次のようにします。

$ awk '/mark:/{gsub(/[^0-9]/,"");print}' file
98

awk で複数行の入力を受け取ろうとしているようなので、次のように実行できます (ファイルに NULL 文字がないことを前提とします)。

$ awk '{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' RS='\0' file
98

RS='\0'、入力レコード区切り文字 ( の「行」を定義するものawk) を に設定します\0。ファイルにはそのような文字がないため、awk一度に全体が読み取られることになります。

答え2

これを機能させるための最小の変更は次のようになります。

cat file | awk '/mark:/{printf( "%s\n",gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

/mark:/ は、「mark:」を含む行を選択するためのものです。
しかし、それではなぜ printf が必要なのでしょうか? これも機能します:

cat file | awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}'

しかし、それは「猫の無駄使い"、awk はファイルから直接読み取ることができるためです。

awk '/mark:/{print(gensub(/^.*mark: ([0-9]+).*$/,"\\1","g"))}' file

編集:

ユーザーのリクエストに応じて: ファイルと文字列に正規表現を使用する方法。

そうですね、あなたが設定したルールでは、gensub のみの awk は不可能です。
また、すべてを括弧内の一致で置き換えるというマッチングの考え方は、.*mark: ([0-9]+).*一部を抽出するにはファイル全体を一致させる必要があることを意味します。これが grep が作成された理由の 1 つです。

次のように使用します:

grep -oP "mark: \K([0-9]+)" file

または:

echo "$string" | grep -oP "mark: \K([0-9]+)"

そして結果が得られます。

関連情報