我正在學習 Linux,我遇到了一個似乎無法獨自解決的挑戰。這裡是:
grep 一個檔案中連續包含 4 個數字但不超過 4 個的行。
我不知道如何解決這個問題。我可以搜尋特定數字,但不能搜尋字串中的數量。
答案1
這個問題有兩種解釋方式;我將解決這兩種情況。您可能想要顯示行:
- 包含四位數字的序列,該序列本身不屬於任何較長的數字序列,或者
- 包含一個四位數字序列,但不再是數字序列(甚至不是單獨的)。
例如,(1) 將顯示1234a56789
,但 (2) 不會。
如果要顯示包含四位數字序列的所有行,而該序列本身不屬於任何較長的數字序列,則一種方法是:
grep -P '(?<!\d)\d{4}(?!\d)' file
這使用Perl正規表示式,Ubuntu 的grep
(GNU grep) 支持通過-P
.它不會匹配諸如 之類的文本12345
,也不會匹配其中的或1234
。2345
但它會匹配1234
in 1234a56789
。
在 Perl 正規表示式中:
\d
[0-9]
表示任何數字(這是or 的縮寫[[:digit:]]
)。x{4}
火柴x
4次。 ({
}
語法不是 Perl 正規表示式特有的;它grep -E
也在擴展正規表示式 via 中。)所以\d{4}
與 相同\d\d\d\d
。(?<!\d)
是一個零寬度負後向斷言。它的意思是「除非前面有\d
」。(?!\d)
是零寬度負前瞻斷言。它的意思是「除非後面跟著\d
」。
(?<!\d)
且不(?!\d)
匹配四位數字序列之外的文字;相反,如果四個數字的序列是較長數字序列的一部分,它們(一起使用時)將阻止其本身進行匹配。
僅使用後視或前視是不夠的,因為最右邊或最左邊的四位子序列仍然會匹配。
使用的好處之一後視斷言和前視斷言是您的模式僅匹配四位數字序列本身,而不匹配周圍的文字。這在使用顏色突出顯示(使用選項)時很有用--color
。
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
預設情況下在 Ubuntu 中,每個使用者都有alias grep='grep --color=auto'
自己的~.bashrc
文件。因此,當您運行以grep
(這是當別名已擴展)和標準輸出是一個終端(這是什麼--color=auto
檢查)。匹配通常以紅色陰影突出顯示(接近朱紅),但我用斜體粗體顯示了它。這是一個螢幕截圖:
您甚至可以使用grep
以下命令僅列印匹配的文本,而不是整行-o
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
另類方式,沒有後視斷言和前視斷言
但是,如果您:
grep
需要一個也能在不支援-P
或不想使用 Perl 正規表示式的系統上運行的命令,和- 不需要專門匹配四位數字 - 如果您的目標只是顯示包含匹配項的行,通常就是這種情況,和
- 可以接受不太優雅的解決方案
……那麼你可以透過以下方式實現這一目標擴展正規表示式反而:
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
這匹配四個數字和它們周圍的非數字字元(或行的開頭或結尾)。具體來說:
[0-9]
符合任何數字(如Perl 正規表示式中的[[:digit:]]
, 或),表示「四次」。因此匹配一個四位數字序列。\d
{4}
[0-9]{4}
[^0-9]
0
匹配不在 到範圍內的字元9
。它相當於Perl 正規表示式中的[^[:digit:]]
(or )\D
。^
,當它沒有出現在[
]
括號中時,匹配行的開頭。同樣,$
匹配行尾。|
方法或者括號用於分組(如代數中)。 so(^|[^0-9])
匹配行的開頭或非數字字符, while($|[^0-9])
匹配行的結尾或非數字字符。
[0-9]{4}
因此,配對僅發生在同時包含四位數字序列 ( ) 的行中:
- 在行的開頭或前面有一個非數字 (
(^|[^0-9])
),和 - 位於行尾或後面跟著非數字 (
($|[^0-9])
)。
另一方面,如果您想要顯示包含四位數字序列但不包含任何超過四位數字的序列(即使是與另一個只有四位數字的序列分開的序列),那麼從概念上講,您的目標是找到與一種模式匹配但不與另一種模式匹配的行。
因此,即使您知道如何使用單一模式,我也建議使用類似的東西馬特的第二個建議,grep
分別針對兩種模式。
這樣做時,您不會從 Perl 正規表示式的任何高級功能中受益匪淺,因此您可能不想使用它們。但為了與上述風格保持一致,這裡有一個縮寫馬特的解決方案使用\d
(和大括號)代替[0-9]
:
grep -P '\d{4}' file | grep -Pv '\d{5}'
由於它使用[0-9]
,馬特的方式更便攜——它可以在grep
不支援 Perl 正規表示式的系統上工作。如果您使用[0-9]
(或[[:digit:]]
) 代替\d
,但繼續使用{
}
,您將更簡潔地獲得 matt 方式的可移植性:
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
單一模式的替代方式
如果您確實更喜歡grep
這樣的命令
- 使用單一正規表示式(不是
grep
用 a 分隔的兩個 s管道, 如上) - 顯示至少包含一個四位數字序列的行,
- 但沒有五個(或更多)數字的序列,
- 而且您不介意匹配整行,而不僅僅是數字(您可能不介意這一點)
....然後你可以使用:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
此-x
標誌使得grep
僅顯示整行匹配的行(而不是任何行)含有一場比賽)。
我使用了 Perl 正規表示式,因為我認為這種情況下可以簡潔\d
並大大提高清晰度。\D
但是如果您需要一些可移植到grep
不支援的系統的東西-P
,您可以用[0-9]
and [^0-9]
(或用[[:digit:]]
and [^[:digit]]
)替換它們:
grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file
這些正規表示式的工作方式是:
中間的
\d{4}
or[0-9]{4}
匹配一組四位數字。我們可能擁有不只一項,但我們至少需要擁有一項。在左側,
(\d{0,4}\D)*
or([0-9]{0,4}[^0-9])*
符合零個或多個*
不超過四位數字的 ( ) 實例,後面跟著一個非數字。零位數字(即沒有)是「不超過四位數字」的一種可能性。這匹配(A)空字串或(二)任何字串結尾以非數字形式且不包含任何超過四位數字的序列。\d{4}
由於緊鄰中心(或)左側的文字[0-9]{4}
必須為空或以非數字結尾,因此這會阻止中心\d{4}
匹配左側有另一個(第五個)數字的四個數字。右邊的
(\D\d{0,4})*
or([^0-9][0-9]{0,4})*
符合零個或多個 (*
) 個非數字實例,後面接著不超過四個數字(與之前一樣,可以是四個、三個、兩個、一個,甚至根本沒有)。這匹配(A)空字串或(二)任何字串開始以非數字形式且不包含任何超過四位數字的序列。由於緊鄰中心右側的文字
\d{4}
(或[0-9]{4}
)必須為空或以非數字開頭,因此這會阻止中心\d{4}
匹配右側有另一個(第五個)數字的四個數字。
這確保了某處存在四位數字序列,並且任何地方都不存在五位或更多數字的序列。
這樣做並沒有壞處,也沒有錯。但也許考慮這種替代方案的最重要原因是它闡明了使用(或類似)代替的好處,如上面和中所建議的grep -P '\d{4}' file | grep -Pv '\d{5}'
馬特的回答。
透過這種方式,很明顯您的目標是選擇包含一件事但不包含另一件事的行。另外,語法更簡單(因此許多讀者/維護者可以更快地理解它)。
答案2
這將顯示連續 4 個數字,但不會顯示更多
grep '[0-9][0-9][0-9][0-9][^0-9]' file
注意^表示不是
雖然我不知道如何解決這個問題,但有一個問題......如果數字是行尾那麼它就不會顯示。
然而,這個醜陋的版本適用於這種情況
grep '[0-9][0-9][0-9][0-9]' file | grep -v [0-9][0-9][0-9][0-9][0-9]
答案3
答案4
如果grep
不支援 perl 正規表示式 ( -P
),請使用下列 shell 指令:
grep -w "$(printf '[0-9]%.0s' {1..4})" file
其中printf '[0-9]%.0s' {1..4}
將產生 4 次[0-9]
.當您有很長的數字並且不想重複該模式時(只需替換4
為要查找的數字位數),此方法很有用。
使用-w
將查找整個單字。但是,如果您對字母數字字串感興趣,例如,則在模式末尾1234a
添加,例如[^0-9]
grep "$(printf '[0-9]%.0s' {1..4})[^0-9]" file