【问题标题】:Find all text between $...$ delimiters using bash script使用 bash 脚本查找 $...$ 分隔符之间的所有文本
【发布时间】:2018-05-30 13:39:55
【问题描述】:

我有一个文本文件,我正在尝试使用 bash 脚本获取包含 $..$ 分隔符(LaTeX 公式)之间的字符串数组。我当前的代码不起作用,结果为空:

#!/bin/bash
array=($(grep -o '\$([^\$]*)\$' test.txt))
echo ${array[@]}

我测试了这个正则表达式here,它找到了匹配项。我使用以下测试字符串:

b5f1e7$bfc2439c621353$d1ce0$629f$b8b5

预期结果是

bfc2439c621353 629f

但是 echo 返回空。虽然如果我使用'[0-9]\+' 它可以工作:

5 1 7 2439 621353 1 0 629 8 5

我做错了什么?

【问题讨论】:

  • 您实际上想要获得 captures,而不是匹配项。 grep 不允许您访问捕获组。使用awk 可以更好地处理分隔文本。如果你想使用正则表达式,Perl 是最好的选择。另请注意,() 是 BRE POSIX 正则表达式模式中的文字 () 符号。
  • 你没有逃脱你的括号 - grep 试图匹配文字 ('s
  • @hek2mgl 这怎么不清楚?我认为这个问题已经得到了清晰的解释,并带有可重现的代码示例。
  • @TomLord 改变主意了,我认为你是对的

标签: regex bash shell grep


【解决方案1】:

怎么样:

grep -o '\$[^$]*\$' test.txt | tr -d '$'

这基本上是执行您原来的grep(但没有括号,导致它不匹配),然后从每个匹配项中删除第一个/最后一个字符。

【讨论】:

    【解决方案2】:

    您可以将awk 与输入字段分隔符一起使用为$

    s='b5f1e7$bfc2439c621353$d1ce0$629f$b8b5'
    
    awk -F '$' '{for (i=2; i<=NF; i+=2) print $i}' <<< "$s"
    

    请注意,此awk 命令不会验证输入。如果您希望awk 只允许有效输入,那么您可以将此gnu awk 命令与FPAT 一起使用:

    awk -v FPAT='\\$[^$]*\\$' '{for (i=1; i<=NF; i++) {gsub(/\$/, "", $i); print $i}}' <<< "$s"
    

    bfc2439c621353
    629f
    

    【讨论】:

    • 我认为 OP 正在尝试在 $...$ 之间查找文本
    • 是的,但是当您找到123$foo 时,您的答案会返回foo - 这不是介于 $s
    • 我已经更新了使用gnu awk 命令的答案,该命令也可以验证输入。相信只有 awksed 可以在单个命令中完成此操作。
    • 看起来不错!也在考虑使用FPAT(因为grep -o,问题中使用的无论如何都是GNU特定的)。
    【解决方案3】:

    这个怎么样?

    grep -Eo '\$[^$]+\$' a.txt | sed 's/\$//g'
    

    我正在使用sed 替换$

    【讨论】:

      【解决方案4】:

      试着摆脱你的大括号:

      tst> grep -o '\$\([^\$]*\)\$' test.txt
      $bfc2439c621353$
      $629f$
      

      当然,然后您必须去掉 $ 标志(-o 打印整个匹配项)。你可以试试 sed:

      tst> sed 's/[^\$]*\$\([^\$]*\)\$[^\$]*/\1\n/g' test.txt
      bfc2439c621353
      629f
      

      【讨论】:

        【解决方案5】:

        为什么你的预期输出是b5f1e7$bfc2439c621353$d1ce0$629f$b8b5 两个元素bfc2439c621353 629f 而不是三个元素bfc2439c621353 d1ce0 629f

        这是一个提取这些的 grep 命令:

        $ grep -Po '\$\K[^\$]*(?=\$)' <<<'b5f1e7$bfc2439c621353$d1ce0$629f$b8b5'
        bfc2439c621353
        d1ce0
        629f
        

        (这需要使用 libpcre 编译的 GNU grep 用于 -P

        这使用\$\K(相当于(?&lt;=\$)向后看第一个$(?=\$)向前看下一个$。由于这些是环视,它们不会被grep在进程,因此可以找到d1ce0

        这是一个用于提取这些的 POSIX sed 命令:

        $ sed 's/^[^$]*\$//; s/\$[^$]*$//; s/\$/\n/g' \
            <<<'b5f1e7$bfc2439c621353$d1ce0$629f$b8b5'
        bfc2439c621353
        d1ce0
        629f
        

        这不使用任何 GNU 表示法,应该适用于任何 POSIX 兼容系统(例如 OS X)。它会删除不需要的开头和结尾部分,然后用换行符替换每个 $

        【讨论】:

        • 因为我正在寻找 LaTeX 公式,所以例如对于字符串 "对于任何 $x\in\mathbb{R}$ 确实 $\sqrt{x^2}=\abs{x}$" 预期结果是 "x\in\mathbb{R}" 并且"\sqrt{x^2}=\abs{x}"
        【解决方案6】:

        使用 bash 正则表达式:

        var="b5f1e7\$bfc2439c621353\$d1ce0\$629f\$b8b5"  # string to var
        while [[ $var =~ ([^$]*\$)([^$]*)\$(.*) ]]       # matching
        do 
            echo -n "${BASH_REMATCH[2]} "                # 2nd element has the match
            var="${BASH_REMATCH[3]}"                     # 3rd is the rest of the string
        done
        echo                                             # trailing newline
        bfc2439c621353 629f
        

        【讨论】:

          猜你喜欢
          • 2021-01-16
          • 2015-10-20
          • 1970-01-01
          • 2018-12-22
          • 1970-01-01
          • 2016-03-30
          • 1970-01-01
          • 2017-03-10
          • 1970-01-01
          相关资源
          最近更新 更多