【问题标题】:How to extract a value from a string using regex and a shell?如何使用正则表达式和外壳从字符串中提取值?
【发布时间】:2011-03-20 05:22:09
【问题描述】:

我在 shell 中,我有这个字符串:12 BBQ ,45 rofl, 89 lol

使用正则表达式:\d+ (?=rofl),结果我想要45

使用正则表达式从字符串中提取数据是否正确?我所做的最好的事情是突出一些在线正则表达式编辑器的价值。大多数时候它会从我的字符串中删除值。

我正在调查expr,但我得到的只是语法错误。

如何在 shell 脚本中提取 45?

【问题讨论】:

  • 恕我直言,为此目的,使用正则表达式是完全可以接受的。
  • 您使用什么工具,您使用什么外壳,您使用的确切命令行是什么以及您遇到的错误是什么?
  • 来自 Unix.SE 的综合回答:unix.stackexchange.com/questions/193223/…

标签: regex shell


【解决方案1】:

您可以使用 GNU grep 的 perl 模式来做到这一点:

echo "12 BBQ ,45 rofl, 89 lol" | grep -P '\d+ (?=rofl)' -o
echo "12 BBQ ,45 rofl, 89 lol" | grep --perl-regexp '\d+ (?=rofl)' --only-matching

-P--perl-regexp 表示 Perl 风格的正则表达式。 -o--only-matching 表示只输出匹配的文本。

【讨论】:

  • 是否可以避免使用 perl 样式,因为自 Mountain Lion 以来它已从 OS X 中的 grep 中删除?
  • OS X 的可能替代方案/解决方法是通过自制软件使用 gnu grep,heystephenwood.com/2013/09/install-gnu-grep-on-mac-osx.html
  • 我可以用docker port c62c1c7b9efb | grep -P '(\d+)$' -o检索docker容器的端口号:D
  • 在busybox中似乎是-E而不是-P
  • 建议使用 BSD grep 进行编辑(例如,在 Mac 上),它将是 -E 而不是 -P,但我相信这不会起作用,因为 -P在 GNU Grep 中用于 Perl 模式,而在(大多数?)greps 中的-E 是扩展模式,这是完全不同的。我刚刚测试了不同版本的-E 选项,它什么也不输出,而不是预期的45
【解决方案2】:

是的,正则表达式当然可以用来提取字符串的一部分。不幸的是,不同风格的 *nix 和不同的工具使用略有不同的正则表达式变体。

这个 sed 命令应该适用于大多数风格(在 OS/X 和 Redhat 上测试)

echo '12 BBQ ,45 rofl, 89 lol' | sed  's/^.*,\([0-9][0-9]*\).*$/\1/g'

【讨论】:

  • 如果您的正则表达式以 .* 开头和结尾,则不需要锚点
  • 也 +1。您的答案是符合 POSIX 的,而接受的答案不是,因为接受的答案使用非标准 -P grep 选项
  • 我在 macOS 上使用它,其中 zsh 是默认值,方法是将命令包装在 /bin/sh -c "command" 中。效果很好!
【解决方案3】:

您似乎在问多个问题。回答他们:

  • 是的,可以使用正则表达式从字符串中提取数据,这就是它们的用途
  • 您遇到错误,您使用的是哪一个以及什么 shell 工具?
  • 您可以通过捕获括号中的数字来提取数字:

    .*(\d+) rofl.*
    

    并使用$1 取出字符串(.* 用于“同一行之前和之后的其余部分)

以 sed 为例,想法变成了这样,用匹配的数字替换文件中的所有字符串:

sed -e 's/.*(\d+) rofl.*/$1/g' inputFileName > outputFileName

或:

echo "12 BBQ ,45 rofl, 89 lol" | sed -e 's/.*(\d+) rofl.*/$1/g'

【讨论】:

  • 在您的示例中,您不需要 .* 中的任何一个。如果您的正则表达式被锚定,您只需要边缘上的那些。未锚定,它将已经匹配字符串中的任何位置。
  • OP 只要求输出号码,而不是成功匹配。通过添加.*,这是一种匹配所有内容并替换为匹配括号中的内容的简单方法。没有它们,字符串的其余部分保持不变,这不是所要求的 (iiuc)。还是我错过了什么?
  • 糟糕,我错过了您为此使用sed。继续。
  • 你使用的是什么 sed 实现? $1 疯了
  • @harold,这是 8 年前的事了,不记得了。 1 美元是疯狂的意思是什么?这就是您在基本上任何正则表达式风格中引用捕获的子字符串的方式。
【解决方案4】:

你可以使用 shell(例如 bash)

$ string="12 BBQ ,45 rofl, 89 lol"
$ echo ${string% rofl*}
12 BBQ ,45
$ string=${string% rofl*}
$ echo ${string##*,}
45

【讨论】:

【解决方案5】:

使用ripgrepreplace 选项,可以将输出更改为捕获组:

rg --only-matching --replace '$1' '(\d+) rofl'
  • --only-matching-o 只输出匹配的部分而不是整行。
  • --replace '$1'-r 将输出替换为第一个捕获组。

【讨论】:

    【解决方案6】:

    您当然可以提取字符串的那一部分,这是解析数据的好方法。正则表达式语法变化很大,因此您需要参考您正在使用的正则表达式的帮助文件。你可以试试这样的正则表达式:

    [0-9]+ *[a-zA-Z]+,([0-9]+) *[a-zA-Z]+,[0-9]+ *[a-zA-Z]+
    

    如果您的正则表达式程序可以进行字符串替换,则将整个字符串替换为您想要的结果,您可以轻松使用该结果。

    您没有提到您使用的是 bash 还是其他 shell。这将有助于在寻求帮助时获得更好的答案。

    【讨论】:

      【解决方案7】:

      您可以使用rextract 使用正则表达式提取并重新格式化结果。

      例子:

      [$] echo "12 BBQ ,45 rofl, 89 lol" | ./rextract '[,]([\d]+) rofl' '${1}'
      45
      

      【讨论】:

      • 如果库是您自己的,您需要添加免责声明(类似于“免责声明:我制作了这个库”)。从 github 看来,这个库/可执行文件是你自己的
      猜你喜欢
      • 1970-01-01
      • 2013-11-13
      • 2020-03-23
      • 1970-01-01
      • 2014-10-17
      • 2021-09-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多