【问题标题】:Using regular expressions in shell script在 shell 脚本中使用正则表达式
【发布时间】:2010-12-10 19:36:43
【问题描述】:

在 linux shell 脚本中使用正则表达式解析字符串的正确方法是什么?我编写了以下脚本以使用curlsed 在控制台上打印我的 SO 代表(不仅仅是因为我很疯狂——我正在尝试在切换到 linux 之前学习一些 shell 脚本和正则表达式)。

json=$(curl -s http://stackoverflow.com/users/flair/165297.json)
echo $json | sed 's/.*"reputation":"\([0-9,]\{1,\}\)".*/\1/' | sed s/,//

但不知何故,我觉得sed 不是在这里使用的合适工具。我听说grep 是关于正则表达式的,并对其进行了一些探索。但显然,只要找到匹配项,它就会打印整行 - 我试图从单行文本中提取一个数字。这是我正在处理的字符串的缩小版本(由curl 返回)。

{"displayName":"Amarghosh","re​​putation":"2,737","badgeHtml":"\u003cspan title=\"1 银徽章\"\u003e\u003cspan class=\"badge2\"\u003e 9679;\u003c/span\u003e\u003cspan class=\"badgecount\"\u003e1\u003c/span\u003e\u003c/span\u003e"}

我想我的问题是:

  • 在 linux shell 脚本中使用正则表达式解析字符串的正确方法是什么?
  • 在这里使用sed 合适吗?
  • 可以使用grep 来完成吗?
  • 还有其他更容易/更合适的命令吗?

【问题讨论】:

    标签: regex linux shell


    【解决方案1】:

    grep 命令将从许多行中选择所需的行,但它不会直接操作该行。为此,您在管道中使用sed

    someCommand | grep 'Amarghosh' | sed -e 's/foo/bar/g'
    

    或者,可以使用awk(或perl,如果可用)。在我看来,它是一个比sed 强大得多的文本处理工具。

    someCommand | awk '/Amarghosh/ { do something }'
    

    对于简单的文本操作,只需坚持使用 grep/sed 组合即可。当您需要更复杂的处理时,请转到awkperl

    我的第一个想法是使用:

    echo '{"displayName":"Amarghosh","reputation":"2,737","badgeHtml"'
        | sed -e 's/.*tion":"//' -e 's/".*//' -e 's/,//g'
    

    sed 进程的数量保持为一个(您可以使用-e 发出多个命令)。

    【讨论】:

    • 我自己是一个 Perl 人,但有时 awk 在提取数据方面更快更干净。它只做一件事,而且做得很好:)
    • @hobbs:你喜欢用正则表达式解析 JSON,而不喜欢 HTML?
    • 谢谢帕克斯。 @Kinopiko 我认为那是因为 json 具有坚实的结构,但 html 可能完全没有结构(缺少右括号等)。
    • 你显然不知道sed。它既有循环,也可以自己进行选择。它和awk 一样“强大”(尽管不如awk 方便),而且肯定比grep 更好。
    • 我非常了解 sed,知道 awk 更适合更复杂的任务 :-) 如果您在谈论 sed 的分支和测试命令,它们是一个可怕的组合,与 awk 的优雅 for 语句完全不同(类似于 C)。任何图灵完备的语言都和其他语言一样“强大”,但我仍然宁愿用 Java 编写我的应用程序而不是机器语言。
    【解决方案2】:

    您可能有兴趣将 Perl 用于此类任务。作为演示,这里是一个打印你想要的数字的 Perl 脚本:

    #!/usr/local/bin/perl
    use warnings;
    use strict;
    use LWP::Simple;
    use JSON;
    
    my $url = "http://stackoverflow.com/users/flair/165297.json";
    my $flair = get ($url);
    my $parsed = from_json ($flair);
    print "$parsed->{reputation}\n";
    

    此脚本要求您安装 JSON 模块,您只需使用命令 cpan JSON 即可。

    【讨论】:

      【解决方案3】:

      要在 shell 脚本中使用 JSON,请使用 jsawk,它类似于 awk,但用于 JSON

      json=$(curl -s http://stackoverflow.com/users/flair/165297.json)
      echo $json | jsawk 'return this.reputation' # 2,747
      

      【讨论】:

      • 谢谢。虽然我认为正则表达式对于这种特殊情况已经足够了,但很高兴知道 shell 有一个 json 解析器。
      【解决方案4】:

      我的提议:

      $ echo $json | sed 's/,//g;s/^.*reputation...\([0-9]*\).*$/\1/'
      

      我在 sed 参数中放了两条命令:

      • s/,//g 用于删除所有逗号,尤其是信誉值中存在的逗号。

      • s/^.*reputation...\([0-9]*\).*$/\1/ 定位行中的信誉值并用该值替换整行。

      在这种特殊情况下,我发现sed 提供了最紧凑的命令,而不会损失可读性。

      其他处理字符串的工具(不仅仅是正则表达式)包括:

      • grep, awk, perl 在大多数其他答案中都提到了
      • tr 用于替换字符
      • cut, paste 用于处理多列输入
      • bash 本身具有用于访问变量的丰富的 $(...) 语法
      • tailhead 用于保留文件的最后一行或第一行

      【讨论】:

      • 谢谢,我不知道我们可以向 sed 传递多个命令。
      【解决方案5】:

      sed 是合适的,但你会为你使用的每个sed 生成一个新进程(在更复杂的场景中这可能太重了)。 grep 不太合适。这是一个使用正则表达式查找感兴趣行的搜索工具。

      Perl 是一个合适的解决方案,它是一种具有强大正则表达式功能的 shell 脚本语言。它可以完成您需要的大部分工作,而无需生成单独的进程(与普通的 Unix shell 脚本不同),并且具有庞大的附加函数库。

      【讨论】:

        【解决方案6】:

        你可以用 grep 来做。 grep 中有 -o 开关,只提取匹配字符串而不是整行。

        $ echo $json | grep -o '"reputation":"[0-9,]\+"' | grep -o '[0-9,]\+'
        2,747
        

        【讨论】:

        • 挑战。只用一个 grep 命令怎么样 :)
        • @qba 感谢-o。 @ghoshdog74 使用一个 grep 和一个 sed 会作弊,对 ;)
        • 我认为向后看是要走的路。 (?<=reputation":")[0-9,]+ 之类的东西,但我不知道 shell 的正则表达式是否支持向后看 - 给定的模式对我不起作用。可能是我没有转义所有特殊字符。
        • @Amarghosh - 作弊??不知道你的意思。无论如何,我的观点是:如果你可以在一次 grep 调用中做到这一点,为什么要这样做 2 次......
        【解决方案7】:

        1) 在 linux shell 脚本中使用正则表达式解析字符串的正确方法是什么?

        包含正则表达式功能的工具包括 sed、grep、awk、Perl、Python 等等。甚至更新版本的 Bash 也具有正则表达式功能。您需要做的就是查看有关如何使用它们的文档。

        2) sed 在这里使用正确吗?

        可以,但不是必须的。

        3) 这可以使用 grep 完成吗?

        是的,它可以。您只需构建与使用 sed 或其他类似的正则表达式即可。请注意,grep 只是做它该做的事情,如果你想修改任何文件,它不会为你做。

        4) 还有其他更简单/更合适的命令吗?

        当然。正则表达式可能很强大,但它不一定是每次都使用的最佳工具。这还取决于您所说的“更容易/适当”是什么意思。 另一种在正则表达式上使用最少的方法是使用字段/分隔符方法。您寻找可以“拆分”的模式。例如,在你的情况下(我已经下载了 165297.json 文件而不是使用 curl ..(但它相同)

        awk 'BEGIN{
         FS="reputation" # split on the word "reputation"
        }
        {
            m=split($2,a,"\",\"")    # field 2 will contain the value you want plus the rest
                                     # Then split on ":" and save to array "a"
            gsub(/[:\",]/,"",a[1])   # now, get rid of the redundant characters
            print a[1]
        }' 165297.json
        

        输出:

        $ ./shell.sh
        2747
        

        【讨论】:

        • "更容易/适当" - 我正在寻找人们通常在 shell 脚本中使用正则表达式进行字符串解析的方式。这是我的第一个 shell 脚本,我在联机手册页的大量帮助下编写了这个脚本。想确保这是执行此操作的正常方式。
        • 唯一需要进行字符串/文本解析的工具就是 awk。
        • 写了@qba 回答中的评论:我似乎无法通过一次 grep 调用来做到这一点 - 怎么做?
        • 只需将第二个 grep 的正则表达式与第一个 grep 的正则表达式结合起来,遗憾的是我不会打扰自己想出。我会让qba给你答案
        【解决方案8】:

        sed 对您的任务来说是一个完全有效的命令,但它可能不是唯一的。

        grep 也可能有用,但正如您所说,它会打印整行。它对于过滤多行文件的行和丢弃不需要的行非常有用。

        高效的 shell 脚本可以使用命令的组合(不仅仅是你提到的两个),利用每个命令的才能。

        【讨论】:

          【解决方案9】:

          盲目地:

          echo $json | awk -F\" '{print $8}'
          

          类似(字段分隔符可以是正则表达式):

          awk -F'{"|":"|","|"}' '{print $5}'
          

          更智能(查找密钥并打印其值):

          awk -F'{"|":"|","|"}' '{for(i=2; i<=NF; i+=2) if ($i == "reputation") print $(i+1)}'
          

          【讨论】:

            【解决方案10】:

            您可以使用适当的库(如其他人所述):

            E:\Home&gt; perl -MLWP::Simple -MJSON -e "print from_json(get 'http://stackoverflow.com/users/flair/165297.json')-&gt;{reputation}"

            $ perl -MLWP::Simple -MJSON -e 'print from_json(get "http://stackoverflow.com/users/flair/165297.json")-&gt;{reputation}, "\n"'

            取决于操作系统/shell 组合。

            【讨论】:

              【解决方案11】:

              通过 Shell 的简单正则表达式

              忽略所讨论的特定代码,有时您可能希望使用 shell 以一种简单的方式,使用类似于 JavaScript 的字符串语法,快速地从标准输入到标准输出进行正则表达式替换。

              以下是一些示例,供正在寻找方法的任何人使用。 Perl 在 Mac 上是一个更好的选择,因为它缺少一些 sed 选项。如果你想将标准输入作为变量,你可以使用MY_VAR=$(cat);

              echo 'text' | perl -pe 's/search/replace/g'; # using perl
              echo 'text' | sed -e 's/search/replace/g'; # using sed

              这是一个自定义的、可重用的正则表达式函数的示例。参数是 source string(或 -- 用于 stdin)、searchreplaceoptions

              regex() {
                  case "$#" in
                      ( '0' ) exit 1 ;; ( '1' ) echo "$1"; exit 0 ;;
                      ( '2' ) REP='' ;; ( '3' ) REP="$3"; OPT='' ;;
                      ( * ) REP="$3"; OPT="$4" ;;
                  esac
                  TXT="$1"; SRCH="$2";
                  if [ "$1" = "--" ]; then [ ! -t 0 ] && read -r TXT; fi
                  echo "$TXT" | perl -pe 's/'"$SRCH"'/'"$REP"'/'"$OPT";
              }
              

              echo 'text' | regex -- search replace g;

              【讨论】:

                猜你喜欢
                • 2016-06-25
                • 1970-01-01
                • 1970-01-01
                • 2012-01-25
                • 2019-03-27
                • 2013-03-29
                • 2013-08-17
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多