web-dev-qa-db-ja.com

sedで貪欲でない(消極的な)正規表現のマッチング?

ドメインだけを抽出するためにsedを使用してURLの行をクリーンアップしようとしています。

だからから:

http://www.suepearson.co.uk/product/174/71/3816/

が欲しいです:

http://www.suepearson.co.uk/

(トレーニングのスラッシュの有無にかかわらず、関係ありません)

私が試してみました:

 sed 's|\(http:\/\/.*?\/\).*|\1|'

そして(欲張りでない量指定子をエスケープする)

sed 's|\(http:\/\/.*\?\/\).*|\1|'

しかし、欲張りでない量指定子が機能するようには思えないので、常に文字列全体に一致することになります。

375
Joel

基本的でも拡張されたPosix/GNU正規表現も欲張りではない数量詞を認識しません。後で正規表現が必要です。幸いなことに、このコンテキストに対するPerlの正規表現は非常に簡単に入手できます。

Perl -pe 's|(http://.*?/).*|\1|'
392
chaos

この特定のケースでは、欲張りでない正規表現を使わずに仕事を終わらせることができます。

[^/]*の代わりにこの欲張りでない正規表現.*?を試してください。

sed 's|\(http://[^/]*/\).*|\1|g'
226
Gumbo

Sedでは、通常、欲張りでない検索は、セパレータまでのセパレータ以外のものを検索することで実装します。

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*\)/.*;\1;p'

出力:

http://www.suon.co.uk

これは:

  • -nを出力しない
  • 検索、パターンのマッチング、置換および印刷s/<pattern>/<replace>/p
  • 入力を簡単にするために、;ではなく/検索コマンド区切り文字を使用します。s;<pattern>;<replace>;p
  • \( ... \)の間の一致を覚えていて、後で\1\2..でアクセス可能です。
  • http://と一致
  • [][ab/]の後に続くものは、ab、または/のいずれかを意味します。
  • ^の最初の[]notを意味するので、[]内のもの以外のものが続きます。
  • だから[^/]/文字以外の何かを意味します
  • *は前のグループを繰り返すため、[^/]*/以外の文字を意味します。
  • これまでのところsed -n 's;\(http://[^/]*\)は検索してhttp://に続く/以外の任意の文字で覚えていることを意味し、あなたが見つけたものを覚えています
  • sed -n 's;\(http://[^/]*\)/'しかし、/を追加するので、ドメインの終わりまで検索したいので、次の/で停止し、最後に別の.*を追加します。
  • グループ1で覚えているマッチ(\1)がドメインになったので、マッチした行をグループ\1に保存されているもので置き換え、print:sed -n 's;\(http://[^/]*\)/.*;\1;p'

ドメインの後に円記号を含める場合は、覚えておくためにグループに円記号をもう1つ追加します。

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*/\).*;\1;p'

出力:

http://www.suon.co.uk/
112
stefanB

sedは "欲張りでない"演算子をサポートしません。

"/"を一致から除外するには、 "[]"演算子を使用する必要があります。

sed 's,\(http://[^/]*\)/.*,\1,'

P.S "/"をバックスラッシュする必要はありません。

36
andcoz

sedの遅延(非欲張り)量指定子のシミュレーション

そして他のすべての正規表現フレーバー!

  1. 式の最初の出現を見つける:

    • POSIX ERE-rオプションを使用)

      正規表現:

      (EXPRESSION).*|.
      

      セッド:

      sed -r "s/(EXPRESSION).*|./\1/g" # Global `g` modifier should be on
      

      例(最初の数字列を見つける)ライブデモ

      $ sed -r "s/([0-9]+).*|./\1/g" <<< "foo 12 bar 34"
      
      12
      

      どのように機能しますか

      この正規表現は、代替|から恩恵を受けます。各位置で、エンジンは代替の最初の側(ターゲット)を探し、それが一致しない場合、ドットのある代替の2番目の側.は次の即時文字に一致します。

      enter image description here

      グローバルフラグが設定されているため、エンジンは入力文字列またはターゲットの最後まで文字ごとに一致を継続しようとします。交替の左側の最初で唯一のキャプチャグループが一致するとすぐに(EXPRESSION)行の残りもすぐに消費されます.*。現在、最初のキャプチャグループで価値を保持しています。

    • POSIX BRE

      正規表現:

      \(\(\(EXPRESSION\).*\)*.\)*
      

      セッド:

      sed "s/\(\(\(EXPRESSION\).*\)*.\)*/\3/"
      

      例(最初の数字列を見つける):

      $ sed "s/\(\(\([0-9]\{1,\}\).*\)*.\)*/\3/" <<< "foo 12 bar 34"
      
      12
      

      これはEREバージョンに似ていますが、交互に関係しません。それで全部です。各単一位置で、エンジンは数字の照合を試みます。

      enter image description here

      見つかった場合、後続のその他の数字が消費されてキャプチャされ、*moreまたはzeroを意味するため、残りの行はすぐに一致します。2番目のキャプチャグループ\(\([0-9]\{1,\}\).*\)*ドット.に到達して単一の文字に一致し、このプロセスが続行されます。

  2. delimited式の最初の出現を見つける:

    このアプローチは、区切られた文字列の最初の出現に一致します。文字列のブロックと呼ぶことができます。

    sed "s/\(END-DELIMITER-EXPRESSION\).*/\1/; \
         s/\(\(START-DELIMITER-EXPRESSION.*\)*.\)*/\1/g"
    

    入力文字列:

    foobar start block #1 end barfoo start block #2 end
    

    -EDE:end

    -SDE:start

    $ sed "s/\(end\).*/\1/; s/\(\(start.*\)*.\)*/\1/g"
    

    出力:

    start block #1 end
    

    最初の正規表現\(end\).*は、最初の終了区切り文字endに一致してキャプチャし、置換はすべて、最後の区切り文字である最近キャプチャされた文字と一致します。この段階での出力はfoobar start block #1 endです。

    enter image description here

    次に、上記のPOSIX BREバージョンと同じ2番目の正規表現\(\(start.*\)*.\)*に結果が渡されます。開始区切り文字startが一致しない場合は単一の文字に一致し、そうでない場合は開始区切り文字に一致してキャプチャし、残りの文字に一致します。

    enter image description here


質問に直接答える

アプローチ2(区切り式)を使用して、2つの適切な式を選択する必要があります。

  • EDE:[^:/]\/

  • SDE:http:

使用法:

$ sed "s/\([^:/]\/\).*/\1/g; s/\(\(http:.*\)*.\)*/\1/" <<< "http://www.suepearson.co.uk/product/174/71/3816/"

出力:

http://www.suepearson.co.uk/
28
revo

複数の文字に対する欲張りでない解決策

このスレッドは本当に古いですが、私は人々がまだそれを必要としていると思います。最初に出現したHELLOまでのすべてを殺したいとしましょう。 [^HELLO]...と言うことはできません。

そこで、top_sekritのように、入力に期待していないユニークなWordを捨てることができると仮定して、Niceソリューションには2つのステップがあります。

この場合、次のことができます。

s/HELLO/top_sekrit/     #will only replace the very first occurrence
s/.*top_sekrit//        #kill everything till end of the first HELLO

もちろん、もっと単純な入力では、もっと小さなWord、あるいはたぶん1つの文字を使うことができます。

HTH!

21
ishahak

これはcutを使って行うことができます。

echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d'/' -f1-3
16
Dee

sed - Christoph Sieghartによる貪欲なマッチング

Sedで欲張りでない一致を得るための秘訣は、一致を終わらせるものを除くすべての文字に一致することです。非常に簡単ですが、貴重な時間を無駄にしてしまいました。Shellスクリプトは、結局のところ、すばやく簡単に実行できるはずです。だから誰かがそれを必要とするかもしれない場合には:

貪欲マッチング

% echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar

欲張りでないマッチング

% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar
15
gresolio

正規表現を使わない別の方法は、fields/delimiterメソッドを使うことです。

string="http://www.suepearson.co.uk/product/174/71/3816/"
echo $string | awk -F"/" '{print $1,$2,$3}' OFS="/"
9
ghostdog74

sedは確かにその場所を持っていますが、これはそれらの1つではありません!

Deeが指摘したように、cutを使うだけです。この場合、はるかに簡単ではるかに安全です。これは、Bash構文を使用してURLからさまざまなコンポーネントを抽出する例です。

url="http://www.suepearson.co.uk/product/174/71/3816/"

protocol=$(echo "$url" | cut -d':' -f1)
Host=$(echo "$url" | cut -d'/' -f3)
urlhost=$(echo "$url" | cut -d'/' -f1-3)
urlpath=$(echo "$url" | cut -d'/' -f4-)

あなたにあげる:

protocol = "http"
Host = "www.suepearson.co.uk"
urlhost = "http://www.suepearson.co.uk"
urlpath = "product/174/71/3816/"

ご覧のとおり、これはもっと柔軟なアプローチです。

(Deeの全クレジット)

5
peterh

純粋な(GNU)sedを使用してこれを解決するという希望はまだあります。これにもかかわらず、これは一般的な解決策ではありませんが、次のように文字列の不要な部分をすべて排除するために「ループ」を使用できます。

sed -r -e ":loop" -e 's|(http://.+)/.*|\1|' -e "t loop"
  • -r:拡張正規表現を使用します(+とエスケープなしの括弧に対して)
  • ":loop": "loop"という名前の新しいラベルを定義します
  • -e:sedにコマンドを追加します
  • "t loop":置換が成功した場合はラベル "loop"に戻ります

ここでの唯一の問題は、最後の区切り文字( '/')も切り捨てることですが、本当に必要な場合は、「ループ」が終了した後で単純に元に戻すことができます。コマンドライン:

-e "s,$,/,"
4
mTUX
sed 's|(http:\/\/[^\/]+\/).*|\1|'
3
Lucero

sed -Eは正規表現を拡張された(現代の)正規表現として解釈します

更新:MacOS Xでは-E、GNU sedでは-r。

3
stepancheg

特にPerlやcutなどの代わりにsedを使用しようとしていると述べたので、グループ化してみてください。これにより、欲張りでない識別子が認識されない可能性があります。最初のグループはプロトコルです(すなわち、 'http://'、 'https://'、 'tcp://'など)。 2番目のグループはドメインです。

 echo "http://www.suon.co.uk/product/1/7/3/" | sed "s | ^ \(。* // \)\([^ /] * \)。* $ |\1\2 |" 

グループ化に慣れていない場合は、ここから始めてください。

2
BrianB

これは、sedを使用して複数文字ストリングの非欲張りなマッチングを堅牢に実行する方法です。たとえば次の入力のように、すべてのfoo...bar<foo...bar>に変更したいとしましょう。

$ cat file
ABC foo DEF bar GHI foo KLM bar NOP foo QRS bar TUV

この出力になるはずです:

ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

これを行うには、fooとbarを個々の文字に変換してから、それらの間のそれらの文字の否定を使用します。

$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/g; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

上記では:

  1. s/@/@A/g; s/{/@B/g; s/}/@C/g{}を入力に存在できないプレースホルダー文字列に変換しているので、それらの文字はfoobarに変換するのに利用できます。
  2. s/foo/{/g; s/bar/}/gfoobarをそれぞれ{}に変換しています
  3. s/{[^{}]*}/<&>/gが欲しい操作を実行しています - foo...bar<foo...bar>に変換する
  4. s/}/bar/g; s/{/foo/g{}foobarに変換しています。
  5. s/@C/}/g; s/@B/{/g; s/@A/@/gは、プレースホルダー文字列を元の文字に変換しています。

最初のステップでそのような文字列を作成するので、入力に含まれていない特定の文字列には依存しません。また、{[^{}]*}を何回でも使用できるので、一致する特定の正規表現の出現箇所には関係ありません。あなたが欲しい実際のマッチを分離するためにそして/またはseds数値マッチ演算子で、式の中で必要です。 2回目だけを置き換える

$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/2; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC foo DEF bar GHI <foo KLM bar> NOP foo QRS bar TUV
1
Ed Morton

私はこれが古いエントリであることを認識していますが、誰かがそれを役に立つと思うかもしれません。完全なドメイン名は253文字を超えないようにしてください。。*を。\ {1、255 \}に置き換えます。

1
Iain Henderson

まだこの答えを見たことがないので、viまたはvimを使ってこれを実行する方法を次に示します。

vi -c '%s/\(http:\/\/.\{-}\/\).*/\1/ge | wq' file &>/dev/null

これはvi:%s置換をグローバルに実行し(末尾のg)、パターンが見つからない場合(e)にエラーを発生させないようにして、結果の変更をディスクに保存して終了します。 &>/dev/nullはGUIが画面上で短く点滅するのを防ぎます。

私はviを超複雑な正規表現に使うことが好きです。 デッド (2)vimは非常に高度な正規表現エンジンを持っています、そして(3)私はすでにvi正規表現に精通しています。日用量編集文書。

0
Luke Davis
echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*|\1|'

気にしないで、私は別のフォーラムでそれを得た:)

0
Dee

別のsedのバージョン:

sed 's|/[:alphanum:].*||' file.txt

これは/の後に英数字が続き(したがって、他のスラッシュはいけません)、行末までの残りの文字と一致します。その後それは何もしないでそれを置き換えます(すなわちそれを削除します)。

0
sycamorex

sed 's|\(http:\/\/www\.[a-z.0-9]*\/\).*|\1|も動きます

0
GL2014

これが2段階のアプローチとawkでできることです。

A=http://www.suepearson.co.uk/product/174/71/3816/  
echo $A|awk '  
{  
  var=gensub(///,"||",3,$0) ;  
  sub(/\|\|.*/,"",var);  
  print var  
}'  

出力: http://www.suepearson.co.uk

それが役立つことを願っています!

0
VINAY NAIR