如何使用sed / grep提取两个单词之间的文本?


134

我试图输出一个字符串,其中包含一个字符串的两个单词之间的所有内容:

输入:

"Here is a String"

输出:

"is a"

使用:

sed -n '/Here/,/String/p'

包括端点,但我不想包含它们。


8
如果输入是Here is a Here String什么,结果应该是什么?还是I Hereby Dub Thee Sir Stringy
ghoti 2012年

5
仅供参考。您的命令意味着在具有单词Here的行和具有字符串String的行之间打印所有内容-而不是您想要的。
Hai Vu 2012年

另一个常见的sed常见问题是“如何提取特定行之间的文本”;这是stackoverflow.com/questions/16643288/...
tripleee

Answers:


109
sed -e 's/Here\(.*\)String/\1/'

2
谢谢!如果我想在“这里是一个字符串”中找到“一个是”和“字符串”之间的所有内容怎么办?(sed -e's / one is(。*)String / \ 1 /'?
user1190650 2012年

5
@ user1190650如果您也想看到“这里是一个”,那将起作用。您可以对其进行测试:echo "Here is a one is a String" | sed -e 's/one is\(.*\)String/\1/'。如果你只是想之间的部分“一个是”和“字符串”,那么你需要做的正则表达式的整条生产线相匹配:sed -e 's/.*one is\(.*\)String.*/\1/'。在sed中,s/pattern/replacement/说“在每行上用'替换'替换'样式'”。它只会更改与“ pattern”匹配的任何内容,因此,如果要替换整行,则需要使“ pattern”与整行匹配。
布莱恩·坎贝尔

9
当输入为Here is a String Here is a String
Jay D

1
很高兴看到一个案例的解决方案:“这里是一个blah blah String这里是1 blah blah String Here是2 a blash blash String”输出应该只选择Here和String之间的第一个子字符串”
Jay D

1
@JayD sed不支持非贪婪匹配,有关建议的替代方法,请参见此问题
Brian Campbell

179

GNU grep还可以支持正面和负面的提前和回溯:对于您的情况,命令为:

echo "Here is a string" | grep -o -P '(?<=Here).*(?=string)'

如果有多次出现Here并且string,你可以选择你是否想从第一场比赛Here和最后的string或单独匹配。在正则表达式的方面,它被称为贪婪匹配(第一情况)非贪婪匹配(第二种情况)

$ echo 'Here is a string, and Here is another string.' | grep -oP '(?<=Here).*(?=string)' # Greedy match
 is a string, and Here is another 
$ echo 'Here is a string, and Here is another string.' | grep -oP '(?<=Here).*?(?=string)' # Non-greedy match (Notice the '?' after '*' in .*)
 is a 
 is another 

31
请注意,GNU grep的-P选项grep在* BSD或任何SVR4(Solaris等)随附的选项中不存在。在FreeBSD中,您可以安装devel/pcre包含的端口,该端口pcregrep支持PCRE(和向前/向后)。OSX的较早版本使用GNU grep,但在OSX Mavericks中,-P是从FreeBSD的版本衍生而来,该版本不包含该选项。
ghoti 2014年

1
嗨,我怎么只提取不同的内容?
Durgesh Suthar

4
这是行不通的,因为如果结束字符串“ string”出现多次,它将得到最后一次出现,而不是下一次出现。
Buttle Butkus

6
在情况下Here is a string a string " is a "" is a string a "有效的答案(忽略引号),按问题的要求。这取决于要选择哪一个,然后答案可能会有所不同。无论如何,对于您的要求,它将起作用:echo "Here is a string a string" | grep -o -P '(?<=Here).*?(?=string)'
anishsane

2
@BND,您需要启用pcregrep的多行搜索功能echo $'Here is \na string' | grep -zoP '(?<=Here)(?s).*(?=string)'
Anishsane

58

接受的答案不会删除之前Here或之后的文本String。这将:

sed -e 's/.*Here\(.*\)String.*/\1/'

主要区别是.*紧接在之前Here和之后String


您的答案很有希望。不过有一个问题。如果同一行中有多个字符串,如何将其提取到第一个看到的字符串?谢谢
Mian Asbat Ahmad Ahmad

@MianAsbatAhmad您可能想*Here和之间做量词,不要String贪婪(或懒惰)。但是,根据 Stackoverflow问题,sed使用的正则表达式类型不支持延迟量词(?紧接在之后.*)。通常,为了实现惰性的量词,您只需要匹配所有您不想匹配的标记,而是匹配所有内容,但是在这种情况下,不仅有单个标记,而且还有整个字符串。String
惠勒

谢谢,我使用awk得到了答案,stackoverflow.com
questions / 51041463 /…

不幸的是,如果字符串有换行符,这将不起作用
Witalo Benicio

不应该这样 .与换行符不匹配。如果你想匹配换行符,可以更换.喜欢的东西[\s\s]
惠勒

35

您可以单独在Bash中剥离字符串:

$ foo="Here is a String"
$ foo=${foo##*Here }
$ echo "$foo"
is a String
$ foo=${foo%% String*}
$ echo "$foo"
is a
$

如果您有一个包含PCRE的GNU grep,则可以使用零宽度的断言:

$ echo "Here is a String" | grep -Po '(?<=(Here )).*(?= String)'
is a

为什么这种方法这么慢?使用此方法剥离大型html页面时,大约需要10秒钟。
亚当·约翰斯

@AdamJohns,哪种方法?PCRE之一?PCRE解析起来相当复杂,但是10秒似乎很极端。如果您担心的话,建议您提出一个包含示例代码的问题,然后看看专家怎么说。
ghoti

我认为这对我来说太慢了,因为它在一个变量中保存了一个很大的html文件的源。当我将内容写入文件然后解析文件时,速度大大提高了。
亚当·约翰斯

22

通过GNU awk,

$ echo "Here is a string" | awk -v FS="(Here|string)" '{print $2}'
 is a 

具有-Pperl-regexp)参数的grep 支持\K,它有助于丢弃先前匹配的字符。在我们的例子中,先前匹配的字符串被Here删除,因此从最终输出中将其丢弃。

$ echo "Here is a string" | grep -oP 'Here\K.*(?=string)'
 is a 
$ echo "Here is a string" | grep -oP 'Here\K(?:(?!string).)*'
 is a 

如果您想要输出,is a那么可以尝试以下方法,

$ echo "Here is a string" | grep -oP 'Here\s*\K.*(?=\s+string)'
is a
$ echo "Here is a string" | grep -oP 'Here\s*\K(?:(?!\s+string).)*'
is a

这不适用于:echo "Here is a string dfdsf Here is a string" | awk -v FS="(Here|string)" '{print $2}',它仅返回is a而不是is a is a@Avinash Raj
alper

20

如果您的文件较长且包含多行,则首先打印数字行会很有用:

cat -n file | sed -n '/Here/,/String/p'

3
谢谢!这是在我的情况下唯一有效的解决方案(多行文本文件,而不是没有换行符的单个字符串)。显然,要使它没有行号,必须省略-nin中的选项cat
Jeffrey Lebowski

...在这种情况下cat可以完全省略;sed知道如何读取文件或标准输入。
三胞胎

9

这可能对您有用(GNU sed):

sed '/Here/!d;s//&\n/;s/.*\n//;:a;/String/bb;$!{n;ba};:b;s//\n&/;P;D' file 

这会在换行符上的两个标记之间(在本例中为HereString)显示文本的每种表示形式,并在文本中保留换行符。


7

上述所有解决方案都有缺陷,其中最后一个搜索字符串在字符串的其他位置重复。我发现最好编写一个bash函数。

    function str_str {
      local str
      str="${1#*${2}}"
      str="${str%%$3*}"
      echo -n "$str"
    }

    # test it ...
    mystr="this is a string"
    str_str "$mystr" "this " " string"

6

您可以使用两个s命令

$ echo "Here is a String" | sed 's/.*Here//; s/String.*//'
 is a 

也可以

$ echo "Here is a StringHere is a String" | sed 's/.*Here//; s/String.*//'
 is a

$ echo "Here is a StringHere is a StringHere is a StringHere is a String" | sed 's/.*Here//; s/String.*//'
 is a 

6

要了解 sed命令,我们必须逐步构建它。

这是你的原文

user@linux:~$ echo "Here is a String"
Here is a String
user@linux:~$ 

让我们尝试Here使用substition选项删除字符串sed

user@linux:~$ echo "Here is a String" | sed 's/Here //'
is a String
user@linux:~$ 

在这一点上,我相信你将能够去除String以及

user@linux:~$ echo "Here is a String" | sed 's/String//'
Here is a
user@linux:~$ 

但这不是您想要的输出。

要组合两个sed命令,请使用-eoption

user@linux:~$ echo "Here is a String" | sed -e 's/Here //' -e 's/String//'
is a
user@linux:~$ 

希望这可以帮助


4

您可以使用\1(请参阅http://www.grymoire.com/Unix/Sed.html#uh-4):

echo "Hello is a String" | sed 's/Hello\(.*\)String/\1/g'

括号内的内容将存储为\1


这将删除字符串,而不是在两者之间输出内容。尝试在sed命令中使用“ is”删除“ Hello”,它将输出“ Hello a”
乔纳森

1

问题。 我存储的Claws Mail消息包装如下,并且我试图提取“主题”行:

Subject: [SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular
 link in major cell growth pathway: Findings point to new potential
 therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is
 Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as
 a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway
 identified [Lysosomal amino acid transporter SLC38A9 signals arginine
 sufficiency to mTORC1]]
Message-ID: <20171019190902.18741771@VictoriasJourney.com>

在此线程中的A2中,如何使用sed / grep提取两个单词之间的文本?只要匹配的文本不包含换行符,下面的第一个表达式就可以“起作用”:

grep -o -P '(?<=Subject: ).*(?=molecular)' corpus/01

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key

但是,尽管尝试了多种变体(.+?; /s; ...),但我无法使它们起作用:

grep -o -P '(?<=Subject: ).*(?=link)' corpus/01
grep -o -P '(?<=Subject: ).*(?=therapeutic)' corpus/01
etc.

解决方案1。

Per 在不同行的两个字符串之间提取文本

sed -n '/Subject: /{:a;N;/Message-ID:/!ba; s/\n/ /g; s/\s\s*/ /g; s/.*Subject: \|Message-ID:.*//g;p}' corpus/01

这使

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular link in major cell growth pathway: Findings point to new potential therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway identified [Lysosomal amino acid transporter SLC38A9 signals arginine sufficiency to mTORC1]]                              

解决方案2. *

我如何使用sed替换换行符(\ n)?

sed ':a;N;$!ba;s/\n/ /g' corpus/01

将用空格替换换行符。

如何使用sed / grep在两个单词之间提取文本,将其与A2链接起来?,我们得到:

sed ':a;N;$!ba;s/\n/ /g' corpus/01 | grep -o -P '(?<=Subject: ).*(?=Message-ID:)'

这使

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular  link in major cell growth pathway: Findings point to new potential  therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is  Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as  a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway  identified [Lysosomal amino acid transporter SLC38A9 signals arginine  sufficiency to mTORC1]] 

此变体删除双倍空格:

sed ':a;N;$!ba;s/\n/ /g; s/\s\s*/ /g' corpus/01 | grep -o -P '(?<=Subject: ).*(?=Message-ID:)'

给予

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular link in major cell growth pathway: Findings point to new potential therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway identified [Lysosomal amino acid transporter SLC38A9 signals arginine sufficiency to mTORC1]]

1
美好的冒险:))
Alexandru-Mihai Manolescu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.