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

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

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

when:* {{a|US}} {{enPR|wĕn|hwĕn}}, {{IPA|/wɛn/|/ʍɛn/}}, {{X-SAMPA|/wEn/|/WEn/}}

我想提取关键字when及其两个国际音标发音,并将它们放在不同的行上:

when wɛn
when ʍɛn

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

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

答案1

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

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

分解(@slm,谢谢)

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

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

    /pattern/!d; s//repl/

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

    /\([^:]*\):.*{IPA|\([^}]*\).*/
    

    该模式将数据分成 2 块。第一个块是when:.这段代码\([^:]*\):表示获取所有字符直到遇到 a:并将其保存在 temp 中。多变的 (\1)。

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

    笔记:任何时候sed你想保存一段字符串,都可以将它括在括号中。它们需要用 a 进行转义,\以便sed知道您的意思不是字面上的括号。就像这样:\( savethis \)

    例子

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

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

    例子

    $ sed '/\([^:]*\):.*{IPA|\([^}]*\).*/!d;s//\1 \2/;s,/,,g;' sample.txt
    when wɛn|ʍɛn
    
  3. 迭代每个 IPA

     :1 s/\(\([^ ]*\).*\)|/\1\n\2 /;t1
    

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

     :label command(s) t label
    

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

  4. 循环内的命令

    如果我们把它拿出label ... loop来一秒钟,然后增加我们的 IPA 示例,使其达到 3,您可以更好地看到发生了什么。

    {{IPA|/wɛn/|/ʍɛn/|/blah/}}
    

    我们将使用之前的命令来完成此操作。

    when wɛn|ʍɛn|blah
    

    如果我们现在运行这个:

    $ echo "when wɛn|ʍɛn|blah" | sed 's/\(\([^ ]*\).*\)|/\1 \2 /;'
    

    我们得到这个:

    when wɛn|ʍɛn
    when blah
    

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

    更简单的例子

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

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

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

    \1您可以通过使用附加的和来扩展该代码片段,从而让自己相信这一点\2

    $ 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
    

    所以循环里面的命令基本上都是走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
    

希望以上内容能够帮助其他路人将来找到这个答案。

答案2

在 Perl 脚本中使用 Perl(处理STDIN

while(<>) {
    if(/^([^:]+):.*{{IPA\|([^}]+)}}/) { 
        print "$1 $_\n" foreach(split /\|/, $2); 
    }
}

或在命令行(管道)上

perl -ne ' if(/^([^:]+):.*{{IPA\|([^}]+)}}/) { print "$1 $_\n" foreach(split /\|/, $2); }'

答案3

使用 bash 和 grep

line='when:* {{a|US}} {{enPR|wĕn|hwĕn}}, {{IPA|/wɛn/|/ʍɛn/}}, {{X-SAMPA|/wEn/|/WEn/}}'
IFS=$': \t' read -ra words <<< "$line"
for item in "${words[@]}"; do
    if [[ $item == "{{IPA|"* ]]; then
        grep -o '/[^/]\+/' <<< "$item" | while read -r pronunc; do
             echo "${words[0]} ${pronunc//\//}"
        done
    fi
done

相关内容