Retirar palavras-chave usando utilitários de linha de comando padrão?

2

Como exemplo, esta é a pronúncia de "when" de acordo com o Wikcionário. enPR, IPA e X-SAMPA são esquemas diferentes para mostrar as pronúncias.

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

Gostaria de extrair a palavra-chave when e suas duas pronúncias do IPA e colocá-las em linhas separadas:

when wɛn
when ʍɛn

Pode haver uma, duas ou mais pronúncias IPA de uma palavra e pode ou não haver enPR ou pronúncias de X-SAMPA.

Estou pensando em PHP, listas dentro de listas, mas isso parece ser um exagero, e eu não quero que os usuários tenham que instalá-lo, se possível. Existe uma maneira de fazer isso no awk, sed, cut ou outro utilitário de linha de comando padrão do Unix?

    
por Yimin Rong 06.08.2013 / 21:54

3 respostas

5

Com sed , você pode escrevê-lo como:

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

Divisão (por @slm, obrigado)

O comando acima pode ser dividido da seguinte forma:

  1. Analise a entrada em when: ... {IPA|...} e exclua linhas não correspondentes.

    Em /pattern/!d; s//repl/

    Nós [d] fazemos o cartão das linhas que não [!] correspondem ao padrão, e então reutilizamos o mesmo padrão no próximo comando de substituição [s] (um padrão vazio significa reutilizar o último padrão). Em vez de [d] eletizar as linhas não correspondentes, poderíamos tê-las deixado inalteradas usando b em vez de d ou, se sabemos que todas as linhas correspondem ao padrão, poderíamos usar s/pattern/repl/ diretamente. / p>

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

    Esse padrão divide os dados em dois blocos. O primeiro pedaço é when: . Esse código, \([^:]*\): diz para pegar todos os caracteres até encontrar um : e salvá-lo em um temp. variável ( ).

    Todos os caracteres entre o : até e incluindo o {IPA| são ignorados. O próximo bit que é salvo é tudo depois do IPA| . Isso é feito por este bloco de código, \([^}]*\) , que diz para salvar todo o código até que um } seja encontrado. Isso é salvo na variável ( ).

    OBSERVAÇÃO: Em sed sempre que você quiser salvar um trecho da string, você poderá envolvê-lo entre parênteses. Eles precisam ser escapados com um \ para que sed saiba que você não se refere a um parêntese literal. Assim: \( savethis \) .

    exemplo

    $ sed 's/\([^:]*\):.*{IPA|\([^}]*\).*/ /;' sample.txt
    when /wɛn/|/ʍɛn/
    
  2. Remover todas as barras ( / )

    Este parece mais complicado porque está usando um separador alternativo. Você normalmente usaria o formulário s///g , mas sed deixa os separadores na hora, então estamos usando vírgulas ( s,,,g ). Este bloco procura por / e substitui-os por nada.

    exemplo

    $ sed '/\([^:]*\):.*{IPA|\([^}]*\).*/!d;s// /;s,/,,g;' sample.txt
    when wɛn|ʍɛn
    
  3. Iterar através de cada IPA

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

    Este é de longe o componente mais complicado desta solução. É difícil ver o que está acontecendo, mas esse bloco é um ramo condicional.

     :label command(s) t label
    

    O rótulo é :1 do (s) comando (s) são s/\(\([^ ]*\).*\)|/\n /; e o t label é o "teste" que vê se o comando anterior modificou o espaço do padrão. Se sim, pule para o label 1 , daí o t1 .

  4. O comando dentro do loop

    Se tirarmos o label ... loop por um segundo e aumentarmos nosso exemplo de IPA para que ele tenha 3, você poderá ver o que está acontecendo um pouco melhor.

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

    Terminaremos com isso, usando os comandos anteriores até este ponto.

    when wɛn|ʍɛn|blah
    

    Se agora executarmos isso:

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

    Nós recebemos isto:

    when wɛn|ʍɛn
    when blah
    

    Você pode ver o que está fazendo agora? Sim, eu também não, então vamos simplificar um pouco mais, pegar a nova linha ( \n ) e trocar algumas strings mais curtas.

    exemplo mais simples

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

    Agora, o que está acontecendo aqui é que o código \(\([^ ]*\).*\)| é inteligente no sentido de que está aninhando os parênteses para que eles fiquem assim ( ( ) ) . O que está sendo combinado nos parênteses internos é qualquer coisa que não seja um espaço. Este get é a string when . Os parens externos correspondem a tudo até o último tubo ( | ).

    A outra coisa interessante com este trecho de código é que os parens são ordenados para que os externos sejam armazenados em enquanto os internos são . Isso ocorre porque sed os números com base na ordem em que eles são encontrados.

    Você pode se convencer disso estendendo o snippet com 's e ' adicionais.

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

    Portanto, o comando dentro do loop basicamente leva o X 2 vezes. Uma vez como parte de todo o X C1|C2 (fora de parênteses) e uma segunda vez como qualquer coisa até o espaço (dentro de parênteses).

  5. Voltar para o ramo condicional

    OK, então o branch vai basicamente chamar o comando em # 5, para IPAs onde há mais de 2. sed 's branch construct vai continuar re-executando o comando até que o comando não modifique mais a substituição, em que ponto ele pára.

    exemplo

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

Espero que os itens acima ajudem outros transeuntes com essa resposta no futuro.

    
por 06.08.2013 / 22:21
2

Com perl dentro de um script perl (processando STDIN )

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

ou na linha de comando (tubulação)

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

Com o bash e o 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
    
por 06.08.2013 / 22:19