В качестве примера, это произношение «когда» согласно Викисловарь. 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'
Вышеуказанная команда может быть разбита следующим образом:
Разбирайте вход в 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/
Удалите все косые черты ( /
)
Это выглядит сложнее, потому что он использует альтернативный разделитель. Обычно вы используете форму s///g
, но sed
позволяет вам создавать разделители «на лету», поэтому вместо этого мы используем запятые ( s,,,g
). Этот блок ищет /
и заменяет их ничем.
$ sed '/\([^:]*\):.*{IPA|\([^}]*\).*/!d;s//\1 \2/;s,/,,g;' sample.txt when wɛn|ʍɛn
Итерации через каждый IPA
:1 s/\(\([^ ]*\).*\)|/\1\n\2 /;t1
Это, безусловно, самый сложный компонент этого решения. Трудно понять, что происходит, но этот блок является условной ветвью.
:label command(s) t label
Метка :1
команда (ы): s/\(\([^ ]*\).*\)|/\1\n\2 /;
и t label
– это «тест», который видит, изменила ли предыдущая команда пространство шаблонов. Если это так, то перейдите к метке 1
, следовательно, t1
.
Команда внутри цикла
Если мы возьмем 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, для 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