2009-11-14 132 views
87

我看到很多關於如何使用sed,awk或gawk進行搜索和替換的示例和手冊頁。如何使用sed,awk或gawk打印只匹配的內容?

但在我的情況下,我有一個正則表達式,我想運行一個文本文件來提取特定的值。我不想做搜索和替換。這是從bash調用的。讓我們用一個例子:

實例的正則表達式:

.*abc([0-9]+)xyz.* 

例輸入文件:

a 
b 
c 
abc12345xyz 
a 
b 
c 

,因爲這聽起來那麼簡單,我無法弄清楚如何調用SED/AWK/GAWK正確。我希望做的,是從我的bash腳本中有:

myvalue=$(sed <...something...> input.txt) 

事情我已經嘗試過包括:

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file 
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing 
+5

哇...人們把這個問題投下來-1?這個問題真的不恰當嗎? – 2009-11-14 09:11:00

+0

使用Regex和強大的命令行工具(如sed/awk或vi,emacs或teco等編輯器)可能更像編程,而不僅僅是使用某些ol應用程序。 IMO屬於SO,超過SU。 – Dereleased 2009-11-14 09:16:31

+0

也許它被拒絕了,因爲它的初始形式沒有明確地定義它的一些要求。它仍然沒有,除非你閱讀OP對答案的評論(包括當事情變成梨形時刪除的那個)。 – pavium 2009-11-14 09:45:31

回答

38

sed鍵(Mac OS X)沒有與+工作。我試圖*而不是和我增加了對印刷匹配p標籤:

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt 

匹配至少有一個數字字符沒有+,我會用:

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt 
+0

謝謝,一旦我使用*而不是+,這對我也有用。 – 2009-11-14 08:59:10

+2

...和打印匹配的「p」選項,這我也不知道。再次感謝。 – 2009-11-14 09:05:56

+2

我不得不逃避'+',然後它對我有用:'sed -n's /^.* abc \([0-9] \ + \)xyz。* $/\ 1/p'' – 2009-11-14 09:23:50

15

我用perl,使這更容易爲自己。例如

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' 

這將運行Perl中,所述-n選項指示的Perl在從STDIN一次在一行讀出並執行該代碼。 -e選項指定要運行的指令。

指令在讀取的行上運行正則表達式,如果匹配則打印出第一套粗體的內容($1)。

你可以做到這一點將多個文件名也結束。例如

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt

+0

謝謝,但我們無法訪問perl,這就是我詢問sed/awk/gawk的原因。 – 2009-11-14 08:50:21

1

如果你想選擇線,然後剝離出位,你不想:

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//' 

它基本上選擇你想要egrep線,然後使用sed脫光了數字之前和之後的位。

您可以在這裏看到這些內容起作用:

pax> echo 'a 
b 
c 
abc12345xyz 
a 
b 
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//' 
12345 
pax> 

更新:顯然,如果你的實際情況比較複雜,所述RE將需要我修改。例如,如果你總是埋內的開始和結束零個或多個非NUMERICS單號:

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//' 
+0

有趣...所以沒有一種簡單的方法來應用複雜的正則表達式並獲取(...)部分中的內容?因爲當我看到你在這裏首先用grep和sed做了什麼,我們的真實情況比放下「abc」和「xyz」要複雜得多。正則表達式被使用,因爲許多不同的文本可以出現在我想提取的文本的任一側。 – 2009-11-14 08:54:06

+0

如果RE真的很複雜,我確定有*更好的方法。也許如果您提供了更多的例子或更詳細的描述,我們可以調整我們的答案以適應。 – paxdiablo 2009-11-14 08:56:46

-3

awk的。我會用下面的腳本:

/.*abc([0-9]+)xyz.*/ { 
      print $0; 
      next; 
      } 
      { 
      /* default, do nothing */ 
      } 
+0

的最後一段,它會得到類似grep的行爲... – dmckee 2009-11-14 09:01:04

+0

這不會輸出數字值'([0-9 +])',這會輸出整行。 – 2013-04-29 20:03:48

-3
gawk '/.*abc([0-9]+)xyz.*/' file 
+2

這似乎並不奏效。它打印整行而不是匹配。 – 2009-11-14 09:55:31

+0

在您的示例輸入文件中,該模式是整個行。對???如果你知道該模式將在特定領域:使用$ 1,$ 2等。例如gawk'$ 1〜/.*abc([0-9]+)xyz。* /'file – ghostdog74 2009-11-14 15:43:20

5

如果你的的grep版本支持它,你可以使用-o選項打印你的正則表達式匹配任何線的一部分。

如果沒有,那麼這裏是最好的sed我能想出:

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//' 

...它刪除,沒有數字/跳過,並且對於剩餘的線,刪除所有開頭和結尾的非數字字符。 (我只是猜測你的意圖是從包含一行的每行中提取數字)。

與類似的問題:

sed -e 's/.*\([0-9]*\).*/&/' 

....或

sed -e 's/.*\([0-9]*\).*/\1/' 

...是sed只支持 「貪婪」 的比賽......所以第一個*會匹配該行的其餘部分。除非我們可以使用否定字符類來實現非貪婪匹配...或具有Perl兼容或其正則表達式的其他擴展的sed版本,否則我們無法從模式空間中提取精確的模式匹配(a線)。

+0

您可以通過這種方式組合兩個'sed'命令:'sed -n's/[^ 0-9] * \([0-9] \ + \)。*/\ 1/p'' – 2009-11-15 04:10:05

+0

以前並不知道關於grep的-o選項。很高興知道。但它打印整個比賽,而不是「(...)」。所以如果你在「abc([[:digit:]] +)xyz」上匹配,那麼你就會得到「abc」和「xyz」以及數字。 – 2009-11-16 19:09:57

-1

可以與外殼

while read -r line 
do 
    case "$line" in 
     *abc*[0-9]*xyz*) 
      t="${line##abc}" 
      echo "num is ${t%%xyz}";; 
    esac 
done <"file" 
2

Perl是最乾淨的語法做到這一點,但如果你沒有perl的(不總是在那裏,我明白了),然後用GAWK和組件的唯一途徑的正則表達式是使用gensub功能。樣本輸入文件的

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file 

輸出將是

12345 

注:gensub替換整個正則表達式(在//之間),所以你需要把*前後([ 0-9] +)除去替換中的數字前後的文本。

+2

一個聰明,可行的解決方案,如果你需要(或想)使用gawk。你注意到了這一點,但要清楚:非GNU awk沒有gensub(),因此不支持這一點。 – cincodenada 2014-01-09 21:56:23

+0

不錯!但是,最好使用match()來訪問捕獲的組。請參閱[我的回答](http://stackoverflow.com/a/39075261/1983854)。 – fedorqui 2016-08-22 10:31:10

28

您可以用sed來做到這一點

sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp' 
  • -n不打印生成的線
  • -r這使得它,所以你沒有逃脫捕獲組的括號()
  • \1捕獲小組賽
  • /g全局匹配
  • /p打印結果

我寫了一個tool爲自己,使這更容易

rip 'abc(\d+)xyz' '$1' 
+2

迄今爲止,這是迄今爲止最好的,也是最好解釋的答案! – 2016-08-18 09:02:25

+0

通過一些解釋,最好理解我們的問題出了什麼問題。謝謝 ! – r4phG 2017-10-11 13:17:52

3

您可以使用awkmatch()訪問被捕獲的組:

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file 
12345 

這試圖匹配模式abc[0-9]+xyz。如果是這樣的話,它將其片存儲在數組matches中,其第一項是塊[0-9]+。由於match()返回子字符串開始位置的字符位置或索引(1,如果它從字符串的開始處開始),它會觸發print操作。


隨着grep,您可以使用一個向後看和前瞻:

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file 
12345 

$ grep -oP 'abc\K[0-9]+(?=xyz)' file 
12345 

當它發生內abcxyz,只是打印數字這檢查模式[0-9]+