Выделение ключевых слов с помощью стандартных утилит командной строки?

В качестве примера, это произношение «когда» согласно Викисловарь. enPR, IPA и X-SAMPA – разные схемы для показа произношения.

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

Я хотел бы выделить ключевое слово, when и его два произношения IPA, и поместить их в отдельные строки:

 when wɛn when ʍɛn 

Могут быть одно, два или несколько произношений IPA слова, и могут быть или не быть произношения enPR или X-SAMPA.

Я думаю, PHP, списки в списках, но это, кажется, слишком много, и я не хочу, чтобы пользователи должны были установить его, если это возможно. Есть ли способ сделать это в awk, sed, cut или другой стандартной утилите командной строки Unix?

С sed вы можете написать это как:

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

Разбивка (by @slm, спасибо)

Вышеуказанная команда может быть разбита следующим образом:

  1. Разбирайте вход в when: ... {IPA|...} и удалите несогласованные строки.

    В /pattern/!d; s//repl/ /pattern/!d; s//repl/

    Мы [d] открываем строки, которые не [!] Соответствуют шаблону, а затем повторно используют один и тот же шаблон в следующей команде подстановки [s] (пустой шаблон означает повторное использование последнего шаблона). Вместо того, чтобы [d] вводить несогласованные строки, мы могли бы оставить их нетронутыми, используя b вместо d , или если мы знаем, что все строки соответствуют шаблону, мы могли бы использовать s/pattern/repl/ напрямую.

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

    Этот шаблон разбивает данные на 2 куска. Первый кусок – это when: Этот бит кода, \([^:]*\): говорит, чтобы взять все символы до тех пор, пока вы не встретите : и сохраните его в темпе. переменная ( \1 ).

    Все символы между : до и включая {IPA| пропускаются. Следующий бит, который был сохранен, – это все после IPA| , Это делается с помощью этого блока кода \([^}]*\) , в котором говорится, что он сохранит весь код до тех пор, пока не будет встречен символ } . Это сохраняется в переменной ( \2 ).

    ПРИМЕЧАНИЕ. В sed любое время, когда вы хотите сохранить фрагмент строки, вы можете заключить ее в круглые скобки. Они должны быть экранированы с помощью \ так что 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 

    Теперь здесь происходит то, что код \(\([^ ]*\).*\)| умный в том смысле, что он гнездяет паренс, чтобы они были такими ( ( ) ) . То, что сравнивается с внутренними parens, – это все, что не является пространством. Это строка get, when строка. Внешние parens соответствуют всем вплоть до последней трубы ( | ).

    Другая интересная вещь с этим фрагментом кода заключается в том, что парсеры упорядочены так, что внешние сохраняются в \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 XXC 

    Таким образом, команда внутри цикла в основном принимает X 2 раза. Однажды, как часть целого X C1|C2 (вне parens), и второй раз, как что-либо до места (внутри parens).

  5. Назад к условной ветви

    ОК, поэтому ветка в основном собирается вызвать команду в # 5, для IPA, где есть более чем 2. Конструкция ветвления sed будет продолжать повторять команду до тех пор, пока команда больше не изменяет подстановку, после чего она перестает ,

    пример

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

Надеюсь, вышесказанное поможет другому прохожим в этом ответе в будущем.

С perl внутри perl-скрипта (обработка STDIN )

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

или в командной строке (трубопроводе)

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

С 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