sedでキャプチャされたグループのみを出力するにはどうすればよいですか?


277

sedキャプチャされたグループのみを出力するように指示する方法はありますか?たとえば、次の入力があるとします。

This is a sample 123 text and some 987 numbers

とパターン:

/([\d]+)/

後方参照でフォーマットされた方法で123および987の出力のみを取得できますか?


グループキャプチャではsed-Eフラグを使用して拡張正規表現をオンにする必要があることに注意してください。
peterh-モニカ

回答:


333

これを機能させるための鍵は、sed出力したくないものを除外し、何をしたいかを指定することです。

string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'

これは言う:

  • デフォルトで各行を印刷しない(-n
  • ゼロ以上の非数字を除外する
  • 1つ以上の数字を含める
  • 1つ以上の非数字を除外する
  • 1つ以上の数字を含める
  • ゼロ以上の非数字を除外する
  • 置換を印刷する(p

一般に、sed括弧を使用してグループをキャプチャし、後方参照を使用してキャプチャしたものを出力します。

echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'

「バー」を出力します。拡張正規表現に-r-EOS Xの場合)を使用する場合、括弧をエスケープする必要はありません。

echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'

最大9つのキャプチャグループとその後方参照が存在できます。後方参照は、グループが表示される順序で番号が付けられていますが、任意の順序で使用でき、繰り返すことができます。

echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'

「バーa」を出力します。

GNUを使用している場合grep(OS Xを含むBSDでも動作する可能性があります):

echo "$string" | grep -Po '\d+'

または次のようなバリエーション:

echo "$string" | grep -Po '(?<=\D )(\d+)'

この-Pオプションは、Perl互換の正規表現を有効にします。man 3 pcrepatternまたはを参照してくださいman 3 pcresyntax


24
注記として、OSX Mountain LionはgrepでPCREをサポートしなくなりました。
yincrash 2012

1
補足として、grep -oオプションはSolaris 9ではサポートされていません。また、Solaris 9ではsed -rオプションはサポートされていません。:(
Daniel Kats

7
システム管理者にgsedのインストールを依頼してください。あなたはいくつかのドーナツがあなたを得るものに驚くでしょう...
avgvstvs

3
'('および ')'の前に '\'を付ける必要がある場合があることに注意してください。理由はわかりません。
ランブリック2014年

7
@lumbric:sed例を参照している場合、-rオプション(または-EOS Xの場合はIIRC)を使用する場合は、括弧をエスケープする必要はありません。違いは、基本正規表現と拡張正規表現(-r)の違いです。
追って通知があるまで一時停止。

55

Sedには最大9つのパターンが記憶されていますが、正規表現の一部を記憶するにはエスケープされた括弧を使用する必要があります。

例と詳細については、こちらをご覧ください


58
sed -e 's/version=\(.+\)/\1/' input.txtこれでもinput.txt全体が出力されます
Pablo

@Pablo、あなたのパターンでは、\+代わりに書く必要があり+ます。また、なぜ-esedコマンドを1つだけ使用するのか理解できません。
Fredrick Gauss

1
用途はsed -e -n 's/version=\(.+\)/\1/p' input.txt以下を参照してください。mikeplate.com/2012/05/09/...
awattar

1
sed -EPerl / Java / JavaScript / Go /その他のフレーバーに非常によく似た、いわゆる「モダン」または「拡張」正規表現を使用することをお勧めします。(と比較grep -Eまたはegrep。)デフォルトの構文は、これらの奇妙なエスケープのルールがあり、「時代遅れ」と考えられています。2つの違いの詳細については、を実行してくださいman 7 re_format
AndrewF 2018年

31

あなたはgrepを使うことができます

grep -Eow "[0-9]+" file

4
@ ghostdog74:絶対にあなたに同意します。キャプチャしたグループのみをgreoに出力させるにはどうすればよいですか?
パブロ

1
@Michael-これがoオプションがある理由です-unixhelp.ed.ac.uk/CGI/man-cgi?grep:-o、--only-matching PATTERNと一致する一致する行の部分のみを表示
バートF

14
@バートF:一致する部分は理解できましたが、キャプチャグループではありません。私が欲しいのは、このようにすることです([0-9] +)。+([abc] {2,3})。2つのキャプチャグループがあります。後方参照などでキャプチャグループのみを出力したい。
パブロ

こんにちは、マイケル。キャプチャされたn番目のグループをgrepで抽出できましたか?
doc_id

1
@Pablo:grepは一致するもののみを出力します。複数のグループを指定するには、複数の式を使用しgrep -Eow -e "[0-9]+" -e "[abc]{2,3}"ます。前のgrepからのパイプ処理を除いて、これらの2つの式を1行に配置するように要求する方法がわかりません(いずれかのパターンが1行で複数回一致する場合でも機能しません) )。
idbrii

13

一連の数字

この回答は、任意の桁数のグループで機能します。例:

$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166

拡大された答え。

キャプチャされたグループのみを出力するようにsedに指示する方法はありますか?

はい。すべてのテキストをキャプチャグループで置き換えます。

$ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123

s/[^0-9]*                           # several non-digits
         \([0-9]\{1,\}\)            # followed by one or more digits
                        [^0-9]*     # and followed by more non-digits.
                               /\1/ # gets replaced only by the digits.

または拡張構文を使用して(バッククォートを減らし、+の使用を許可):

$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123

番号がないときに元のテキストを印刷しないようにするには、次を使用します。

$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
  • (-n)デフォルトでは入力を印刷しません。
  • (/ p)置換が行われた場合にのみ印刷します。

そして、いくつかの数字を一致させる(そしてそれらを印刷する)には:

$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456

これは、任意の桁数の実行に対して機能します。

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166

これは、grepコマンドと非常によく似ています。

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166

\ dについて

とパターン: /([\d]+)/

Sedは '\ d'(ショートカット)構文を認識しません。上記[0-9]で使用されているASCIIの同等物は、完全に同等ではありません。唯一の代替ソリューションは、文字クラス '[[:digit:]] `を使用することです。

選択した回答は、このような「文字クラス」を使用してソリューションを構築します。

$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'

このソリューションは、(正確に)2桁の数字に対してのみ機能します。

もちろん、答えはシェル内で実行されているため、このような答えを短くするためにいくつかの変数を定義できます。

$ str='This is a sample 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p"

ただし、すでに説明したように、s/…/…/gpコマンドを使用する方が適切です。

$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987

これは、数字の繰り返し実行と、short(er)コマンドの書き込みの両方をカバーします。


高い投票率で受け入れられた回答を読んだ後、驚いた私は下にスクロールして、その狭い範囲について書き、質問の精神に実際に取り組みました。私は、誰かがすでに何年も前にそれをしているだろうと思ったはずです。これは非常によく説明されており、正解です。
Amit Naidu、

9

質問で与えられたパターンは単なる例であり、目標はどのパターンにも一致することでした。

パターンスペースに改行を挿入できるGNU拡張機能を使用したsedがある場合、1つの提案は次のとおりです。

> set string = "This is a sample 123 text and some 987 numbers"
>
> set pattern = "[0-9][0-9]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
123
987
> set pattern = "[a-z][a-z]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
his
is
a
sample
text
and
some
numbers

これらの例は、CYGWIN でのtcsh(そう、私その間違ったシェルを知っています)を使用しています。(編集:bashの場合、セットと=の前後のスペースを削除します。)


@Joseph:ただし、私のタスクに基づいて、ghostdog74が示唆するように、grepの方がより自然な感じがします。一致全体ではなく、キャプチャグループのみをgrepで出力する方法を理解する必要があります。
パブロ

2
単なるメモですが、プラス記号「+」は「1つ以上」を意味し、パターンで繰り返す必要がなくなります。したがって、「[0-9] [0-9] *」は「[0-9] +」になります
RandomInsano 2012

4
@RandomInsano:を使用するには+、それをエスケープするか、-rオプション(-EOS Xの場合)を使用する必要があります。また、使用することができます\{1,\}(または-rまたは-Eエスケープせず)。
追って通知があるまで一時停止。

9

Perlをあきらめて使用する

sedはそれをカットしないので、タオルを投げてPerlを使用してみましょう。少なくともそれはLSBですがgrepGNU拡張はそうではありません:-)

  • 一致する部分全体を印刷します。一致するグループや後読みは必要ありません。

    cat <<EOS | perl -lane 'print m/\d+/g'
    a1 b2
    a34 b56
    EOS

    出力:

    12
    3456
  • 1行に1つの一致、多くの場合構造化されたデータフィールド:

    cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g'
    a1 b2
    a34 b56
    EOS

    出力:

    1
    34

    後読みあり:

    cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
    a1 b2
    a34 b56
    EOS
  • 複数のフィールド:

    cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g'
    a1 c0 b2 c0
    a34 c0 b56 c0
    EOS

    出力:

    1 2
    34 56
  • 1行に複数の一致があり、多くの場合、非構造化データ:

    cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
    a1 b2
    a34 b56 a78 b90
    EOS

    出力:

    1 
    34 78

    後読みあり:

    cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
    a1 b2
    a34 b56 a78 b90
    EOS

    出力:

    1
    3478

1
「sedを使って」という質問の最後で何がわかりませんでしたか?
Moonchild

@Moonchild Google社員は気にしません。
Ciro Santilli郝海东冠状病六四事件法轮功

1
これは重宝しました。コマンドラインの正規表現のすべての問題をsedで解決する必要があるわけではありません。
PPPaul

5

試す

sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"

私はcygwinの下でこれを得ました:

$ (echo "asdf"; \
   echo "1234"; \
   echo "asdf1234adsf1234asdf"; \
   echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \
  sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"

1234
1234 1234
1 2 3 4 5 6 7 8 9
$

2

これは、OPが要求したもの(グループをキャプチャすること)ではありませんが、次を使用して数値を抽出できます。

S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'

次のようになります。

123
987
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.