使用标准命令行实用程序提取关键字?

Yim*_*ong 2 sed awk text-processing regular-expression

例如,根据维基词典,这是“when”的发音。enPR、IPA 和 X-SAMPA 是不同的发音方案。

when:* {{a|US}} {{enPR|w?n|hw?n}}, {{IPA|/w?n/|/??n/}}, {{X-SAMPA|/wEn/|/WEn/}}
Run Code Online (Sandbox Code Playgroud)

我想提取关键字when及其两个 IPA 发音,并将它们放在单独的行中:

when w?n
when ??n
Run Code Online (Sandbox Code Playgroud)

一个词可能有一个、两个或多个 IPA 发音,可能有也可能没有 enPR 或 X-SAMPA 发音。

我在考虑 PHP,列表中的列表,但这似乎有点矫枉过正,如果可能的话,我不希望用户必须安装它。有没有办法在 awk、sed、cut 或其他标准的 Unix 命令行实用程序中做到这一点?

Sté*_*las 5

使用sed,您可以将其写为:

sed '/\([^:]*\):.*{IPA|\([^}]*\).*/!d;s//\1 \2/;s,/,,g;:1
     s/\(\([^ ]*\).*\)|/\1\n\2 /;t1'
Run Code Online (Sandbox Code Playgroud)

细分(@slm,谢谢)

上面的命令可以分解如下:

  1. 解析输入when: ... {IPA|...}并删除不匹配的行。

    /pattern/!d; s//repl/

    我们 [d] 丢弃不[!] 匹配模式的行,然后在下一个 [s] 替换命令中重用相同的模式(空模式意味着重用最后一个模式)。与其 [d] 删除不匹配的行,我们可以通过使用b代替使它们保持不变d,或者如果我们知道所有行都匹配模式,我们可以s/pattern/repl/直接使用。

    /\([^:]*\):.*{IPA|\([^}]*\).*/
    
    Run Code Online (Sandbox Code Playgroud)

    该模式将数据分成 2 个块。第一个块是when:. 这段代码\([^:]*\):说要获取所有字符,直到遇到 a:并将其保存在临时文件中。变量 ( \1)

    :到 和之间的所有字符{IPA|都将被跳过。保存的下一个位是IPA|. 这是由这个代码块完成的,\([^}]*\),它说保存所有代码,直到}遇到 a 。这保存在变量 ( \2) 中。

    注意:sed任何时候你想保存一段字符串,你可以把它用括号括起来。他们需要用 a 进行转义,\以便sed知道您不是指字面意义上的括号。像这样:\( savethis \)

    例子

    $ sed 's/\([^:]*\):.*{IPA|\([^}]*\).*/\1 \2/;' sample.txt
    when /w?n/|/??n/
    
    Run Code Online (Sandbox Code Playgroud)
  2. 删除所有正斜杠 ( /)

    这个看起来更复杂,因为它使用了备用分隔符。您通常会使用 form s///g,但sed让您即时组成分隔符,因此我们使用逗号 ( s,,,g)代替。此块搜索/并用空替换它们。

    例子

    $ sed '/\([^:]*\):.*{IPA|\([^}]*\).*/!d;s//\1 \2/;s,/,,g;' sample.txt
    when w?n|??n
    
    Run Code Online (Sandbox Code Playgroud)
  3. 遍历每个 IPA

     :1 s/\(\([^ ]*\).*\)|/\1\n\2 /;t1
    
    Run Code Online (Sandbox Code Playgroud)

    这是迄今为止该解决方案中最复杂的部分。很难看出发生了什么,但这个块是一个条件分支。

     :label command(s) t label
    
    Run Code Online (Sandbox Code Playgroud)

    标签是:1命令s/\(\([^ ]*\).*\)|/\1\n\2 /;t label是查看前一个命令是否修改了模式空间的“测试”。如果是,则跳转到 label 1,因此t1.

  4. 循环内的命令

    如果我们label ... loop拿出一秒钟,并增加我们的 IPA 示例使其具有 3,您可以更好地看到正在发生的事情。

    {{IPA|/w?n/|/??n/|/blah/}}
    
    Run Code Online (Sandbox Code Playgroud)

    到此为止,我们将使用前面的命令结束此操作。

    when w?n|??n|blah
    
    Run Code Online (Sandbox Code Playgroud)

    如果我们现在运行这个:

    $ echo "when w?n|??n|blah" | sed 's/\(\([^ ]*\).*\)|/\1 \2 /;'
    
    Run Code Online (Sandbox Code Playgroud)

    我们得到这个:

    when w?n|??n
    when blah
    
    Run Code Online (Sandbox Code Playgroud)

    你能看到它现在在做什么吗?是的,我也没有,所以让我们稍微简化一下,去掉换行符 ( \n) 并换入一些较短的字符串。

    更简单的例子

    $ echo "X C1|C2|C3" | sed 's/\(\([^ ]*\).*\)|/\1 \2 /;'
    X C1|C2 X C3
    
    Run Code Online (Sandbox Code Playgroud)

    现在这里发生的事情是代码\(\([^ ]*\).*\)|很聪明,因为它嵌套了括号,因此它们就像这样( ( ) )。在内部括号中匹配的是任何不是空格的东西。这是when字符串。外括号匹配直到最后一个管道 ( |) 的所有内容。

    这个代码片段的另一个有趣的事情是对括号进行排序,以便外部的存储在里面,\1而内部的存储在\2. 这是因为sed根据遇到它们的顺序对它们进行编号。

    您可以通过使用附加的\1's 和\2's扩展代码段来说服自己。

    $ echo "X C1|C2|C3" | sed 's/\(\([^ ]*\).*\)|/\1 \1 \1 /;'
    X C1|C2 X C1|C2 X C1|C2 C3
    
    $ echo "X C1|C2|C3" | sed 's/\(\([^ ]*\).*\)|/\1 \2 \2 /;'
    X C1|C2 X X C
    
    Run Code Online (Sandbox Code Playgroud)

    所以循环内的命令基本上是取了X2次。一次作为整体的一部分X C1|C2(外括号),第二次作为空间的任何内容(内括号)。

  5. 回到条件分支

    好的,所以分支基本上会调用 #5 中的命令,对于 IPA 的数量超过 2.sed的分支构造将继续重新运行命令,直到命令不再修改替换,此时它停止。

    例子

    $ echo "X C1|C2|C3" | sed ':1 s/\(\([^ ]*\).*\)|/\1\n\2 /; t1'
    X C1
    X C2
    X C3
    
    Run Code Online (Sandbox Code Playgroud)

希望以上内容能帮助其他路人在未来得到这个答案。