web-dev-qa-db-ja.com

sedで捕獲したグループだけを出力するにはどうすればいいですか?

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

This is a sample 123 text and some 987 numbers

そしてパターン:

/([\d]+)/

後方参照によってフォーマットされた方法で123と987の出力だけを取得できますか?

245
Pablo

これを機能させる鍵は、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/'

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

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

最大9つのキャプチャグループとそれらの後方参照があります。後方参照はグループの出現順に番号が付けられていますが、それらは任意の順序で使用でき、繰り返し使用できます。

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

"a bar 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 を参照してください。

281

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

例と詳細については、 こちら を参照してください。

51
Peter McG

grepが使えます

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

私は質問で与えられたパターンはほんの一例にすぎず、目標はanyに一致することであったと思います。 - )パターン。

パターンスペースに改行を挿入できるように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(はい、Iが知っているのシェル)です。 (編集:bashの場合は、setと=の前後のスペースを削除します。)

8
Joseph Quinsey

Perlをあきらめて使ってください

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

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

    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
    

数字の並び

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

$ 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桁の数字に対してのみ機能します。

もちろん、答えはシェル内で実行されているので、そのような答えを短くするために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

それは繰り返し実行される数字と短い(er)コマンドを書くことの両方をカバーするでしょう。

7
Arrow

やってみる

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
$
5
Bert F

それは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
2
Thomas Bratt