sed、awk、またはgawkを使用して、一致するものだけを印刷する方法


100

sed、awk、またはgawkを使用して検索と置換などを行う方法について、多くの例とmanページを参照しています。

しかし、私の場合、特定の値を抽出するためにテキストファイルに対して実行する正規表現があります。検索と置換を行いたくありません。これはbashから呼び出されています。例を使用してみましょう:

正規表現の例:

.*abc([0-9]+)xyz.*

入力ファイルの例:

a
b
c
abc12345xyz
a
b
c

これは簡単に聞こえますが、sed / awk / gawkを正しく呼び出す方法がわかりません。私が望んでいたことは、私のbashスクリプト内からです:

myvalue=$( sed <...something...> input.txt )

私が試したことは次のとおりです。

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing

10
うわー...人々はこの質問に-1を投票しましたか?それは本当に質問の不適切ですか?
ステファン

Regexや、sed / awkなどの強力なコマンドラインユーティリティや、vi、emacs、tecoなどのエディターを使用することは、olアプリケーションを使用するだけでなく、プログラミングに似ている場合があります。IMOこれはSUよりもSOに属しています。
2009年

おそらく、当初の形式では要件の一部が明確に定義されていなかったため、不承認となった可能性があります。回答に対するOPのコメント(物事がナシの形になったときに削除したものを含む)を読まない限り、それはまだしません。
パビウム2009年

回答:


42

sed(Mac OS X)はで動作しませんでした+*代わりに試し、pマッチを印刷するためのタグを追加しました:

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt

なしで少なくとも1つの数字と一致させる+には、次のようにします。

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt

+の代わりに*を使用すると、これもうまくいきました。
ステファン

2
...そして「p」オプションを使用して、一致を印刷します。これについても知りませんでした。再度、感謝します。
–Stéphane

2
私は脱出しなければならなかった、+そしてそれはそれのために私のために働いた:sed -n 's/^.*abc\([0-9]\+\)xyz.*$/\1/p'
さらなる通知まで一時停止した。

3
これは、最新のRE形式を使用していないため、+は標準文字であり、{、}構文で表現する必要があるためです。-E sedオプションを使用して、最新のREフォーマットをトリガーできます。チェックre_format(7)、DESCRIPTIONの特に最後の段落developer.apple.com/library/mac/#documentation/Darwin/Reference/...
anddam

33

sedを使用してこれを行うことができます

 sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp'
  • -n 結果の行を印刷しません
  • -rこれにより、キャプチャグループの括弧がエスケープされなくなります()
  • \1 キャプチャグループの一致
  • /g グローバルマッチ
  • /p 結果を印刷する

これを簡単にするためのツールを自分で作成しました

rip 'abc(\d+)xyz' '$1'

3
これはこれまでのところ最高で、最もよく説明された答えです!
Nik Reiman

いくつかの説明がありますが、私たちの問題の何が間違っているのかを理解する方が良いでしょう。ありがとうございました !
r4phG 2017年

17

私はperlこれを自分で簡単にするために使用します。例えば

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/'

これはPerl -nを実行します。オプションは、STDINから一度に1行ずつ読み取り、コードを実行するようにPerlに指示します。-eオプションは、実行するための命令を指定します。

この命令は、読み取った行で正規表現を実行し、一致する場合、最初のセットのブラケット($1)の内容を出力します。

最後に複数のファイル名を指定することもできます。例えば

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt


おかげで、私たちはperlにアクセスできません。そのため、sed / awk / gawkについて質問しました。
–Stéphane

5

のバージョンがgrepサポートしている場合は、-oオプションを使用して、正規表現に一致する行の部分のみを印刷できます。

そうでなければ、ここがsed私が思いつくことができる最高のものです:

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

...数字なしで削除/スキップし、残りの行については、先頭と末尾のすべての非数字文字を削除します。(私はあなたの意図が1を含む各行から数を抽出することであることを推測しているだけです)。

次のような問題:

sed -e 's/.*\([0-9]*\).*/&/' 

....または

sed -e 's/.*\([0-9]*\).*/\1/'

...はsed「貪欲な」一致のみをサポートするということです...したがって、最初の。*は残りの行と一致します。否定された文字クラスを使用して貪欲でない一致を達成できる場合を除き...またはsed正規表現に対するPerl互換またはその他の拡張機能を備えたバージョンの場合、パターンスペース(行)。


あなたは自分の2組み合わせることができsed、このようにコマンドを:sed -n 's/[^0-9]*\([0-9]\+\).*/\1/p'
追って通知があるまで一時停止しました。

以前は、grepの-oオプションについて知りませんでした。知っておくといい。ただし、「(...)」ではなく、一致全体を出力します。したがって、「abc([[:digit:]] +)xyz」に一致する場合、「abc」と「xyz」および数字が表示されます。
ステファン・

を思い出させてくれてありがとうgrep -o!私はこれをしようとしていて、sedいくつかの行で複数の一致を見つける必要性に苦労していました。私の解決策は、stackoverflow.com
a / 58308239/117471

3

を使用awkmatch()て、キャプチャされたグループにアクセスできます。

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file
12345

これはパターンに一致しようとしますabc[0-9]+xyz。その場合、スライスを配列に格納しmatchesます[0-9]+。その最初の項目はブロックです。match() その部分文字列が始まる場所(文字列の先頭から始まる場合は1)の文字位置またはインデックスを返すので、printアクションがトリガーされます。


ではgrep、あなたは、ルックビハインドおよびルックアヘッドを使用することができます。

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file
12345

$ grep -oP 'abc\K[0-9]+(?=xyz)' file
12345

このチェックパターン[0-9]+、それが内部に発生するabcxyz、ちょうど数字を出力します。


2

perlは最もクリーンな構文ですが、perlがない場合(常にそこにあるとは限りません)、gawkと正規表現のコンポーネントを使用する唯一の方法は、gensub機能を使用することです。

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file

サンプル入力ファイルの出力は

12345

注:gensubは正規表現全体(//の間)を置き換えるため、置換の数値の前後のテキストを削除するには、([0-9] +)の前後に。*を配置する必要があります。


2
gawkを使用する必要がある(または使用したい)場合は、賢くて実行可能なソリューションです。あなたはこれに気づきましたが、明確にするために、GNU以外のawkにはgensub()がないため、これをサポートしていません。
cincodenada 2014年

いいね!ただし、match()キャプチャされたグループへのアクセスにはを使用するのが最適な場合があります。これについては私の答えを参照しください。
fedorqui「SO害をやめる」

1

行を選択したい場合は、不要なビットを取り除きます。

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//'

基本的には、必要な行を選択し、番号の前後のビットを取り除くためにegrep使用sedします。

あなたはここでこれを実際に見ることができます:

pax> echo 'a
b
c
abc12345xyz
a
b
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//'
12345
pax> 

更新:実際の状況がさらに複雑な場合は、REを変更する必要があります。たとえば、最初と最後に常に1つの数値が0個以上の非数値に埋め込まれている場合:

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'

興味深い...それで、複雑な正規表現を適用して(...)セクションにあるものだけを取得する簡単な方法はありませんか?ここで最初にgrepで、次にsedで何をしたのかを確認していると、実際の状況は「abc」と「xyz」を削除するよりもはるかに複雑です。抽出したいテキストの両側に多くの異なるテキストが表示される可能性があるため、正規表現が使用されます。
ステファン

REが本当に複雑な場合、もっと良い方法がある私は確信しています。おそらく、さらにいくつかの例またはより詳細な説明を提供していただければ、それに合わせて回答を調整できます。
paxdiablo 2009年

0

OPのケースでは、1つの行に複数の一致が存在する可能性があるとは規定されていませんが、Googleトラフィックの場合は、その例も追加します。

OPはパターンからグループを抽出する必要があるため、使用grep -oするには2パスが必要です。しかし、私はこれが仕事を成し遂げる最も直感的な方法だとまだ思います。

$ cat > example.txt <<TXT
a
b
c
abc12345xyz
a
abc23451xyz asdf abc34512xyz
c
TXT

$ cat example.txt | grep -oE 'abc([0-9]+)xyz'
abc12345xyz
abc23451xyz
abc34512xyz

$ cat example.txt | grep -oE 'abc([0-9]+)xyz' | grep -oE '[0-9]+'
12345
23451
34512

プロセッサ時間は基本的に無料ですが、人間の可読性は非常に貴重なので、「1年後、これはどうなると思いますか?」という質問に基づいてコードをリファクタリングする傾向があります。実際、私が公開またはチームと共有する予定のコードman grepについては、長いオプションが何であるかを理解し、それらを置き換えることもできます。そのようです:grep --only-matching --extended-regexp


-1

あなたはシェルでそれを行うことができます

while read -r line
do
    case "$line" in
        *abc*[0-9]*xyz* ) 
            t="${line##abc}"
            echo "num is ${t%%xyz}";;
    esac
done <"file"

-3

awkの場合。次のスクリプトを使用します。

/.*abc([0-9]+)xyz.*/ {
            print $0;
            next;
            }
            {
            /* default, do nothing */
            }

これは数値を出力せず([0-9+])、行全体を出力します。
マークラカタ2013

-3
gawk '/.*abc([0-9]+)xyz.*/' file

2
これは機能していないようです。一致ではなく行全体を印刷します。
–Stéphane

サンプル入力ファイルでは、そのパターンは行全体です。正しい???パターンが特定のフィールドにあることがわかっている場合:$ 1、$ 2などを使用します。例:gawk '$ 1〜/.*abc([0-9]+)xyz.*/' file
ghostdog74
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.