【问题标题】:How to use sed/grep to extract text between two words?如何使用 sed/grep 提取两个单词之间的文本?
【发布时间】:2012-10-25 21:36:40
【问题描述】:

我正在尝试输出一个字符串,该字符串包含字符串的两个单词之间的所有内容:

输入:

"Here is a String"

输出:

"is a"

使用:

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

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

【问题讨论】:

  • 如果输入是Here is a Here String,结果应该是什么?还是I Hereby Dub Thee Sir Stringy
  • 仅供参考。您的命令意味着打印包含单词 Here 的行和包含单词 String 的行之间的所有内容 - 不是您想要的。
  • 另一个常见的sed 常见问题解答是“如何在特定行之间提取文本”;这是stackoverflow.com/questions/16643288/…

标签: string bash sed grep


【解决方案1】:

GNU grep 还可以支持正负前瞻和回溯: 对于您的情况,命令将是:

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

如果Herestring 出现多次,您可以选择是要从第一个Here 到最后一个string 进行匹配,还是单独匹配它们。就正则表达式而言,它被称为greedy match (first case)non-greedy match (second case)

$ 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 

【讨论】:

  • 请注意,GNU grep 的 -P 选项不存在于 *BSD 中包含的 grep 或任何 SVR4(Solaris 等)附带的选项中。在 FreeBSD 中,您可以安装 devel/pcre 端口,其中包括支持 PCRE(和前瞻/后视)的 pcregrep。旧版本的 OSX 使用 GNU grep,但在 OSX Mavericks 中,-P 派生自 FreeBSD 的版本,不包含该选项。
  • 嗨,我如何只提取不同的内容?
  • 这不起作用,因为如果您的结束字符串“string”出现多次,它将获得 last 出现,而​​不是 next发生。
  • 如果是Here is a string a string,根据问题要求,两者 " is a "" is a string a " 都是有效答案(忽略引号)。这取决于您想要哪一个,然后答案可能会有所不同。无论如何,根据您的要求,这将起作用:echo "Here is a string a string" | grep -o -P '(?&lt;=Here).*?(?=string)'
  • @BND,您需要启用multi-line search feature of pcregrepecho $'Here is \na string' | grep -zoP '(?&lt;=Here)(?s).*(?=string)'
【解决方案2】:
sed -e 's/Here\(.*\)String/\1/'

【讨论】:

  • 谢谢!如果我想在“Here is a one is a String”中找到“one is”和“String”之间的所有内容怎么办? (sed -e 's/one is(.*)String/\1/' ?
  • @user1190650 如果您也想看到“这里是一个”,那也可以。你可以测试一下:echo "Here is a one is a String" | sed -e 's/one is\(.*\)String/\1/'。如果您只想要“one is”和“String”之间的部分,那么您需要使正则表达式匹配整行:sed -e 's/.*one is\(.*\)String.*/\1/'。在 sed 中,s/pattern/replacement/ 说“在每行上用 'replacement' 替换 'pattern'”。它只会改变任何匹配“pattern”的东西,所以如果你想让它替换整行,你需要让“pattern”匹配整行。
  • 当输入为Here is a String Here is a String时会中断
  • 很高兴看到一个案例的解决方案:“Here is a blah blah String Here is 1 a blah blah String Here is 2 a blash blash String”输出应该只拾取之间的第一个子字符串这里和字符串"
  • @JayD sed 不支持非贪婪匹配,请参阅this question 了解一些推荐的替代方案。
【解决方案3】:

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

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

主要区别在于在Here 之前和String 之后添加.*

【讨论】:

  • 您的回答很有希望。一个问题。如果同一行中有多个字符串,如何将其提取到第一个看到的字符串?谢谢
  • @MianAsbatAhmad 您可能希望在HereString 之间创建非贪婪(或懒惰)的* 量词。但是,根据this Stackoverflow 问题,sed 使用的正则表达式类型不支持惰性量词(紧跟在.* 之后的?)。通常要实现一个惰性量词,您只需匹配除您不想匹配的标记之外的所有内容,但在这种情况下,不只是一个标记,而是一个完整的字符串,String
  • 谢谢,我用 awk 得到了答案,stackoverflow.com/questions/51041463/…
  • 不幸的是,如果字符串有换行符,这不起作用
  • 不应该这样。 . 不匹配换行符。如果要匹配换行符,可以将. 替换为[\s\s]
【解决方案4】:

您可以单独剥离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 秒似乎很极端。如果你担心,我推荐你pose a question 包括示例代码,看看专家怎么说。
  • 我认为这对我来说太慢了,因为它在一个变量中保存了一个非常大的 html 文件的源代码。当我将内容写入文件然后解析文件时,速度显着提高。
  • 应该是公认的答案,因为它使用纯 Bash。
【解决方案5】:

通过 GNU awk,

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

带有-P(perl-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
【解决方案6】:

如果您有一个包含许多多行出现的长文件,首先打印数字行很有用:

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

【讨论】:

  • 谢谢!这是在我的情况下唯一有效的解决方案(多行文本文件,而不是没有换行符的单个字符串)。显然,要让它没有行号,cat 中的-n 选项必须省略。
  • ... 在这种情况下,cat 可以完全省略; sed 知道如何读取文件或标准输入。
【解决方案7】:

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

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

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

【讨论】:

    【解决方案8】:

    你可以使用两个 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 
    

    【讨论】:

      【解决方案9】:

      要理解sed命令,我们必须一步一步地构建它。

      这是你的原文

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

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

      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 命令,请使用 -e 选项

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

      希望对你有帮助

      【讨论】:

        【解决方案10】:

        上述所有解决方案都存在缺陷,即最后一个搜索字符串在字符串的其他地方重复。我发现最好写一个 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"
        

        【讨论】:

          【解决方案11】:

          您可以使用\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”
          【解决方案12】:

          问题。我存储的 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,How to use sed/grep to extract text between two words? 下面的第一个表达式“有效”,只要匹配的文本不包含换行符:

          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。

          Extract text between two strings on different lines

          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.*

          How can I replace a newline (\n) using sed?

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

          将用空格替换换行符。

          将其与 How to use sed/grep to extract text between two words? 中的 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]]
          

          【讨论】:

          • 不错的冒险 :))
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-06-20
          • 1970-01-01
          • 1970-01-01
          • 2015-03-11
          • 1970-01-01
          • 2017-12-13
          • 2014-10-03
          相关资源
          最近更新 更多