web-dev-qa-db-ja.com

SED:同じ行に複数のパターン、最初のパターンを照合/解析する方法

私は電話番号のデータを保持するファイルと、いくつかの役に立たないものを持っています。電話番号を解析しようとしていますが、電話番号/回線が1つしかない場合は問題ありません。しかし、複数の数値がある場合、sedは最後の数値と一致し(最初のパターンにのみ一致する必要があるとどこにでもあると言われていても)、他の数値を取得できません。

私のdata.txt:

bla bla bla NUM:09011111111 bla bla bla bla NUM:08022222222 bla bla bla

データを解析するときの最初のアイデアは、最初の電話番号の前にあるすべての「初期」「bla bla bla」を削除することでした(つまり、「NUM:」の最初の出現を検索します)。その後、すべてのものを削除します電話番号の後に、番号を取得します。その後、残りの文字列から次の出現を解析したいと思います。

だから今私がそれをsedしようとすると、私はいつも行の最後の番号を取得します:

>sed 's/.*NUM://' data.txt
08022222222 bla bla bla
> 

主に私はSEDの私の理解の何が悪いのかを理解したいと思います。もちろん、より効率的な提案を歓迎します!私のsedコマンドは、 'NUM:'の前のすべてのものを ''(空)で置き換えませんか?なぜ常に最後に一致するのですか?

ありがとう!

16
julumme

これはあなたのために働くかもしれません:

echo "bla bla bla NUM:09011111111 bla bla bla bla NUM:08022222222 bla bla bla" |
sed 's/NUM:/\n&/g;s/[^\n]*\n\(NUM:[0-9]*\)[^\n]*/\1 /g;s/.$//'
NUM:09011111111 NUM:08022222222

あなたが持っている問題は、.*は貪欲です。つまり、最長一致に一致しますnot最初の一致。ユニークな文字(\n sedはそれを行区切り文字として使用するため、対象の文字列(NUM:...)そして、その固有の文字ではないすべてのものを削除します[^\n]*の後に一意の文字\n、文字列を扱いやすい断片に効果的に分割します。

22
potong

今までにご存じのとおり、sed正規表現は貪欲であり、私が知る限り、貪欲にすることはできません。

これまで実現されていなかった2つの代替策は、この種のマッチング/抽出に他のツールを使用することです。

Perlは、-peパラメータを使用したsedのドロップイン置換として使用できます。 ? non-greedy修飾子をサポートします:

$ Perl -pe 's/.*?NUM://' data.txt
09011111111 bla bla bla bla NUM:08022222222 bla bla bla

-oオプションをGNU grepに使用すると、正規表現に一致するデータのビットのみを取得できます。

$ egrep -o 'NUM:[0-9]*' data.txt 
NUM:09011111111
NUM:08022222222
12
Eduardo Ivanec

数字がNUM:に続く数字で定義されている場合:

sed -n -e 's/$/\n/' -e ':begin' \
  -e 's/\(NUM:[0-9][0-9]*\)\(.*\)\n\(.*\)/\2\n\3 \1/' \
  -e 'tbegin' -e 's/.*\n //' -e '/NUM/p'

これは何ですか:

  1. マーカーとして機能する\nを行末に配置します。
  2. マーカーの前の数字を見つけて、行末(マーカーの後)に配置してください。
  3. 番号が見つかった場合は、上記の2に進みます。
  4. マーカーの前に数字が残っていない場合は、数字の前のすべてを削除してください。
  5. 数値が行にある場合は、それを印刷します(数値が見つからない場合に対処するため)。

それはまた逆に行うことができ、最初に番号なしの行をドロップします:

sed  -e '/NUM/!d' -e 's/$/\n/' -e ':begin' \
  -e 's/\(NUM:[0-9][0-9]*\)\(.*\)\n\(.*\)/\2\n\3 \1/' \
  -e 'tbegin' -e 's/.*\n //'
3
jfg956

次のパターンを使用できます。

sed -r 's/^(.*NUM:)(.*NUM:.*)$/\2/'
0
kev