regex - with - regular greedy




非貪婪(不情願)正則表達式在sed中匹配? (13)

模擬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
      

      它是如何工作的

      這個正則表達式受益於替換| 。 在每個位置,引擎將尋找交替的第一面(我們的目標),並且如果它不匹配具有點的交替的第二面. 匹配下一個直接字符。

      由於設置了全局標誌,因此引擎會嘗試逐個字符地繼續匹配輸入字符串或目標的末尾。 一旦交替左側的第一個和唯一的捕獲組匹配(EXPRESSION)行的其餘​​部分立即被消耗。 我們現在在第一捕獲組中保持我們的價值。

    • POSIX BRE

      正則表達式:

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

      桑達:

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

      示例(查找第一個數字序列):

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

      這一個就像ERE版本,但沒有涉及更改。 就這樣。 在每個單獨的位置引擎都會嘗試匹配一個數字。

      如果找到,則其他後面的數字被消耗並且被捕獲,並且其餘的行被立即匹配,否則,因為*意味著更多或零它跳過第二捕獲組\(\([0-9]\{1,\}\).*\)*並到達一個點. 以匹配單個字符,並繼續此過程。

  2. 查找首次出現的分隔表達式:

    這種方法將匹配第一次出現的分隔字符串。 我們可以稱之為一個字符串塊。

    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

    然後將結果傳遞給第二個正則表達式\(\(start.*\)*.\)* ,它與上面的POSIX BRE版本相同。 如果起始分隔符start不匹配,它匹配單個字符,否則匹配並捕獲起始分隔符並匹配其餘字符。

直接回答你的問題

使用方法#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/

我正在嘗試使用sed來清理網址以提取域名..

所以來自:

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

我想要:

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

(無論是否有訓練斜線,都沒關係)

我努力了:

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

和(逃避非貪婪量詞)

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

但我似乎無法讓非貪婪量詞工作,所以它總是匹配整個字符串。


非單一字符的非貪婪解決方案

這個線程真的很老,但我認為人們仍然需要它。 讓我們說你想殺死所有東西,直到第一次發生HELLO 。 你不能說[^HELLO]你好[^HELLO] ...

所以一個很好的解決方案包含兩個步驟,假設你可以省掉一個你不希望輸入的獨特角色,比如說` (反引號)。

在這種情況下,我們可以:

s_HELLO_`_     #will only replace the very first occurrence
s_.*`__        #kill everything till end of the first HELLO

HTH!


sed不支持“非貪婪”操作符。

您必須使用“[]”運算符將“/”從匹配中排除。

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

PS沒有必要反斜杠“/”。


sed - 由Christoph Sieghart非貪婪匹配

在sed中獲得非貪婪匹配的技巧是匹配除終止匹配的字符之外的所有字符。 我知道,這是一個毫不費力的事情,但我浪費了寶貴的時間,畢竟,shell腳本應該快速簡單。 所以如果有人可能需要它:

貪婪的匹配

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

非貪婪匹配

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

sed當然有它的位置,但這不是其中之一!

正如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)


仍然有希望使用純粹的(GNU)sed來解決這個問題。 儘管在某些情況下這不是一個通用的解決方案,但您可以使用“循環”來消除字符串中所有不必要的部分,如下所示:

sed -r -e ":loop" -e 's|(http://.+)/.*|\1|' -e "t loop"
  • -r:使用擴展正則表達式(用於+和未轉義的括號)
  • “:loop”:定義一個名為“loop”的新標籤
  • -e:向sed添加命令
  • “t循環”:如果替換成功,則跳回標籤“循環”

這裡唯一的問題是它會削減最後一個分隔符('/'),但是如果你確實需要它,你仍然可以簡單地在“循環”結束後放回去,只需在前一個末尾附加這個附加命令命令行:

-e "s,$,/,"

另一種方式,不使用正則表達式,是使用字​​段/分隔符方法,例如

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

因為你明確表示你正在嘗試使用sed(而不是perl,cut等),請嘗試分組。 這規避了可能不被識別的非貪婪標識符。 第一組是協議(即'http://','https://','tcp://'等)。 第二組是域名:

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

如果你不熟悉分組,請從here開始。


對於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/
  • 第一個^ in []意思not ,所以除了[]的東西之外
  • 所以[^/]意味著除/字符以外的任何內容
  • *是重複上一組,因此[^/]*表示除/之外的字符。
  • 到目前為止, sed -n 's;\(http://[^/]*\)表示搜索並記住http://後跟除/之外的任何字符,並記住您找到的內容
  • 我們要搜索直到域的末尾,所以停在下一個/所以添加另一個/最後: sed -n 's;\(http://[^/]*\)/'但我們想匹配域名之後的其餘部分如此添加.*
  • 現在在組1( \1 )中記住的比賽是域,因此用組\1保存的東西替換匹配的線並且打印: sed -n 's;\(http://[^/]*\)/.*;\1;p'

如果你想在域之後加入反斜杠,那麼在組中添加一個反斜杠來記住:

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

輸出:

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

我意識到這是一個舊的條目,但有人可能會覺得它有用。 由於完整的域名不得超過253個字符的總長度,請使用。\ {1,255 \}替換。*。


這可以使用cut來完成:

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


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




regex-greedy