提取數字之前和下劃線之後的字串

提取數字之前和下劃線之後的字串

原來的字串是這樣的:

str-str001-002_01
str-str005-006_05

我想在數字之前和下劃線之後提取字串,所以它會是這樣的:

str-str_01
str-str_05

我記得 sed 可以將模式分成這樣的組:

 sed -n 's/\(^.*\)\([0-9]-[0-9]\)\(.*$\)/\1\3/p'

但它列印:

str-str0002_01

然後我記得[0-9]只是一個數字,所以我用+號或*號嘗試了它。然後它給出空結果。

ps:透過使用

echo 'str-str001-002_01' | sed -n 's/\(^.*\)\([0-9]-[0-9]\)\(.*$\)/\2/p'

我可以看到它匹配1-0

然後我嘗試了:

echo 'str-str001-002_01' | sed -n 's/\(^.*\)\([0-9]\+-[0-9]\+\)\(.*$\)/\2/p'

它留下了前 2 個數字,並且只匹配

1-002

那麼如何使其匹配001-002

答案1

這提供了所需的輸出:

sed -nE 's/^([^0-9]*).*_([^_]+)$/\1_\2/p'

您的範例的輸出

str-str_01
str-str_05

解釋

  • sed -nE 's/…/…/p'- 使用 ERE,除非它們匹配,否則不要列印行
  • ^- 錨定到行的開頭
  • ([^0-9]*)- 匹配盡可能長的模式,即至少一個非數字字符
  • .*_- 盡可能匹配(包括什麼都不匹配),後面跟“ _
  • ([^_]+)- 匹配盡可能長的不是底線的模式(至少一個字元)
  • $- 錨定到行尾
  • \1_\2- 將整行替換為第一個(…)匹配項「_」和第二個(…)匹配項

您的嘗試未按預期工作的原因是因為*(and +) 是貪婪的 - 它將消耗盡可能多的與前面的原子匹配的字元。因此,對於(.*)([0-9]+)應用於類似的ERE abc123.* 將消耗abc12,留下[0-9]+匹配3。你需要一個「不是數字" 限制第一個符合:([^0-9]*)([0-9]+)得到abc123

答案2

$ cat file
str-str001-002_01
str-str005-006_05
$ sed 's/[0-9]\{3\}-[0-9]\{3\}//' file
str-str_01
str-str_05

這裡的替換指令是匹配並刪除NNN-NNN其中NNN的三位數。

匹配最後一個數字,用以1,代替3

$ sed 's/[0-9]\{1,\}-[0-9]\{1,\}//' file
str-str_01
str-str_05

這對應於+在擴展正則表達式中的使用。預設使用的正規表示式sed是「基本」正規表示式,並且+會符合文字加字元。大多數sed實作也支援擴展表達式-E

$ sed -E 's/[0-9]+-[0-9]+//' file
str-str_01
str-str_05

使用*,如[0-9]*-[0-9]*,將不起作用,因為它會匹配破折號str-str(其周圍有零位數字)。


如果您覺得確實必須匹配整條線並捕獲您想要保留的位,那麼您也可以這樣做。以下命令捕獲初始非數字和最後一位,包括下劃線:

$ sed 's/\([^0-9]*\).*\(_.*\)/\1\2/' file
str-str_01
str-str_05

然而,恕我直言,這有點難以破譯,並對您在問題中從未提到的字符串的開頭和結尾做出假設。例如,開頭不能包含要刪除的數字之前的數字,並且字串的結尾將在最後的下劃線,如果字串的該部分中有多個下劃線,則不必在要刪除的數字之後。

您始終可以進一步添加此表達式以確保僅NNN-NNN不捕獲該位,但這會使該表達式更難以理解。

相關內容