【问题标题】:What command line tool should I use to extract a string from a file using a complex regexp我应该使用什么命令行工具来使用复杂的正则表达式从文件中提取字符串
【发布时间】:2017-01-19 07:57:37
【问题描述】:

问题

在一个 linux 脚本中,我想用文件中的数据分配几个变量。

该命令应该能够:

  • 使用复杂的正则表达式:表示普通的 java/javascript/php/perl 正则表达式, 我可以适应一些变化
  • 在标准输出返回第一个捕获组(或参数指定的捕获组)。如果它支持命名捕获组可以接收名称作为参数:`regextractor ""
  • 理想情况下应该已经存在于标准 linux 发行版中,也许在 osx 上也是如此

示例

我正在寻找类似的命令

mydate=`regextractor "^date:\s*(\S{10}).*$" myfile.md`
mytitle=`regextractor "^title:\s*(.*)\s*$" myfile.md`
echo $mydate - $mytitle

我的文件:

---
slug: article1
date: 2012-01-29 15:34:01
title: What is the best monetary system invented til now?
author: raisercostin<raisercostin@gmail.com>
tags: currency,monetary,system
type: question
toslug: article

这里测试了正则表达式:https://regex101.com/r/y311eP/1

我试过了

  • 更新:grep - 默认 grep 使用简化的正则表达式 grep -o "toslug:\(.*\)" myfile.md => toslug: article
  • 更新:grep 扩展 - 它支持复杂的正则表达式但不捕获组 grep --color=no -oE "toslug:(.*)" myfile.md => toslug: article
  • sed - 它不支持捕获组,因此返回一行。 sed -nE "s/^date:\s*(\S*)//p" myfile.md 更新:它实际上支持捕获组,但您需要使用 -E 参数启用 --regexp-extended
  • awk - 它不支持捕获组,因此返回一行 awk '/^date:\s*(.*)$/' myfile.md

更新

  • 在我的Linux vagrant-ubuntu-trusty-64 3.13.0-101-generic #148-Ubuntu SMP Thu Oct 20 22:08:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 上:
    • sed 没有记录 -E 选项,但适用于某些情况
    • 工作 sed -nE 's/^date:\s*(\S*)/\1/p' myfile.md => 2012-01-29 15:34:01
    • 不起作用 sed -nE 's/^date:\s*(\S{10})/\1/p' myfile.md => 2012-01-29 15:34:01
    • 不起作用 sed -nE 's/^date:\s*(\S*)$/\1/p' myfile.md => 没有行
    • 工作 awk '{ match($0, /^date:\s*(\S{10}).*$/, a); if(a[1])print a[1]}' myfile.md => 2012-01-29
  • 在我的Darwin costins-MBP.router1 16.3.0 Darwin Kernel Version 16.3.0: Thu Nov 17 20:23:58 PST 2016; root:xnu-3789.31.2~1/RELEASE_X86_64 x86_64 上:
    • 部分工作 sed -nE 's/^date:\s*(\S*)/\1/p' myfile.md => 2012-01-29 15:34:01 - 初始空间未被占用
    • 不起作用 sed -nE 's/^date:\s*(\S{10})/\1/p' myfile.md => 没有行
    • 不起作用 sed -nE 's/^date:\s*(\S*)$/\1/p' myfile.md => 没有行
    • 不起作用 awk '{ match($0, /^date:\s*(\S{10}).*$/, a); if(a[1])print a[1]}' myfile.md => awk: syntax error at source line 1{ match($0, &gt;&gt;&gt; /^date:\s*(\S{10}).*$/, &lt;&lt;&lt;

【问题讨论】:

  • sed 支持捕获组。在替换中使用\1 来获取该组的匹配项。
  • 你对 \1 的存在是正确的,它只有在 -E 传递了一个未在我的 ubuntu 发行版中记录的参数时才有效。无论如何,似乎还有其他限制:行的开始/结束和匹配的字符数。
  • 我从来不需要使用-E
  • sed 使用 BRE,所以需要在 ( 之前使用反斜杠来制作捕获组。
  • 如果我删除 -E 我会得到以下 sed -n 's/^date:\s*(\S*)$/\1/p' myfile.md sed: -e expression #1, char 22: invalid reference \1 on s' 命令的 RHS` 。这就是为什么我一开始说它不支持捕获组。

标签: regex


【解决方案1】:

这是一个 perl oneliner:

perl -E 'undef$/;$_=<>;($d,$t)= $_ =~  /\ndate:\s*(\S{10}).+\ntitle:\s*(.+?)\R/s;say "$d - $t";' <file.txt 

输出:

2012-01-29 - What is the best monetary system invented til now?

文件.txt

---
slug: article1
date: 2012-01-29 15:34:01
title: What is the best monetary system invented til now?
author: raisercostin<raisercostin@gmail.com>
tags: currency,monetary,system
type: question
toslug: article

【讨论】:

  • 谢谢。它在带有perl -E 'undef$/;$_=&lt;&gt;;($d,$t)= $_ =~ /date:\s*(\S{10}).*$/s;say "[$d$t]";' &lt;myfile.md 的ubuntu 上工作,但如果正则表达式以^ 开头,就像perl -E 'undef$/;$_=&lt;&gt;;($d,$t)= $_ =~ /^date:\s*(\S{10}).*$/s;say "[$d$t]";' &lt;myfile.md 一样。但至少在 ubuntu 和 osx 上的行为是相同的。
  • 它在 ubuntu 和 osx 上都与修饰符 m: perl -E 'undef$/;$_=&lt;&gt;;($d,$t)= $_ =~ /^date:\s*(\S{10}).*$/m;say "[$d$t]";' &lt;myfile.md 一起使用。谢谢!
【解决方案2】:

(gnu) awk 支持捕获:

awk '/^date/ { match($0, /^date:[[:blank:]]*([^[:blank:]]{10}).*$/, a); print a[1]}' myfile.md

给:2012-01-29

awk '/title/ { match($0, /^title:[[:blank:]]*([^[:blank:]]*.*$)/, a); print a[1]}' myfile.md

给:What is the best monetary system invented til now?

【讨论】:

  • 谢谢。它与不需要使用/^date/ 进行初始过滤的变体完美配合。它适用于:awk '{ match($0, /^date:\s*(\S{10}).*$/, a); if(a[1])print a[1]}' myfile.mdawk '{ match($0, /^title:\s*(.*).*$/, a); if(a[1])print a[1]}' myfile.md
  • 不幸的是,这在 osx Darwin costins-MBP.router1 16.3.0 Darwin Kernel Version 16.3.0: Thu Nov 17 20:23:58 PST 2016; root:xnu-3789.31.2~1/RELEASE_X86_64 x86_64 上不起作用。我收到awk: syntax error at source line 1 context is /title/ { match($0, &gt;&gt;&gt; /^title:[[:blank:]]*([^[:blank:]]*.*$)/, &lt;&lt;&lt; 错误
  • 为避免 sed/awk 变体出现问题,您可以轻松地使用 ruby​​:ruby -ne 'puts $1 if /^date:\s*(\S{10}).*$/' myfile.mdruby -ne 'puts $1 if /^title:\s*(\S*.*$)/' myfile.md
【解决方案3】:

基于 Jan Smydke 的回复。这匹配两个组。

输入:

regextractor () {
    perl -E 'undef$/;$_=<>;($v1,$v2)= $_ =~ /'$1'/m;say "$v1$v2";' <$2
}
regextractor "^date:\s*(\S{10}).*$" myfile.md
regextractor "^title:\s*(.*)\s*$" myfile.md

输出:

2012-01-29
What is the best monetary system invented til now?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-15
    • 2017-09-09
    • 1970-01-01
    • 2010-09-12
    • 2014-10-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-02
    相关资源
    最近更新 更多