Como fazer uma substituição de sed (s /// g) baseada em uma lista? Eu preciso trocar várias palavras, com outras palavras correspondentes

0

Eu não acho que essa pergunta tenha sido feita antes, então não sei se sed é capaz disso.

Suponha que eu tenha um monte de números em uma frase que eu preciso expandir em palavras, um exemplo prático é trocar as citações numeradas em um ensaio típico em formato MLA:

essay.txt :

Sentence 1 [1]. sentence two [1][2]. Sentence three[1][3].

Key.txt (este é um arquivo delimitado por tabulação):

1   source-one
2   source-two
3   source-three
...etc

% esperadoResult.txt:

Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three]

Aqui está minha tentativa de pseudocódigo, mas não entendi o suficiente sobre sed ou tr para fazer isso da maneira correta:

 cat essay.txt | sed s/$(awk {print $1} key.txt)/$(awk {print $2} key.txt)/g

PS: Se há um truque no notepad ++ para encontrar e substituir em massa usando vários termos, isso seria ótimo. Assim, parece que o localizar e substituir funciona apenas para um termo de cada vez, mas eu preciso de uma maneira de fazê-lo em massa para muitos termos de uma só vez.

    
por Tom 22.09.2016 / 05:36

3 respostas

1

awk pode ser efetivamente igual a perl aqui um pouco mais simples , embora implementações diferentes do GNU possam desperdiçar um pouco de tempo de CPU desnecessariamente dividindo o ?) arquivo de texto:

awk 'NR==FNR{a["\["$1"\]"]="["$2"]";next} {for(k in a) gsub(k,a[k]);print}' key.txt essay.txt

Desde que você solicitou explicação :

  • awk opera pegando um 'script' que consiste em pares de ação padrão e lê um ou mais arquivos (ou entrada padrão) um 'registro' por vez, onde por padrão cada registro é uma linha, e para cada registro o divide em campos por padrão no espaço em branco (o que inclui a guia) e aplica o script por vez (a menos que indicado de outra forma) testando cada padrão (que geralmente analisa o registro atual e / ou seus campos) e se ele corresponde executando a ação (que geralmente faz algo para ou com o registro e / ou campos). Aqui eu especifico dois arquivos key.txt essay.txt para que eles leiam esses dois arquivos nessa ordem, linha por linha. O script pode ser colocado em um arquivo em vez de na linha de comando, mas aqui eu escolhi não.

  • o primeiro padrão é NR==FNR . NR é uma variável incorporada que é o Número do Registro sendo processado; FNR é similarmente o número do registro dentro do arquivo de entrada atual. Para o primeiro arquivo ( key.txt ) estes são iguais; para o segundo arquivo (e quaisquer outros) eles são desiguais

  • a primeira ação é {a["\["$1"\]"]="["$2"]";next} . awk tem matrizes 'associativas' ou 'hash'; arrayname[subexpr] onde subexpr é uma expressão com valor de cadeia lê ou define um elemento da matriz. $number , por ex. $1 $2 etc referencia os campos e $0 referencia o registro inteiro. Por acima, esta ação é executada apenas para linhas em key.txt , por exemplo, na última linha do arquivo $1 é 3 e $2 é source-three , e isso armazena uma entrada de matriz com um índice de \[3\] e um conteúdo de [source-three] ; veja abaixo por que eu escolhi esses valores. Os "\[" e "\]" são literais de string usando escores cujos valores reais são \[ e \] , enquanto "[" "]" são apenas [ ] e operandos de cadeia sem nenhum operador entre eles são concatenados. Finalmente, esta ação executa next , o que significa ignorar o resto do script para este registro, basta voltar ao início do loop e começar no próximo registro.

  • o segundo padrão está vazio, portanto, ele corresponde a todas as linhas do segundo arquivo e executa a ação {for(k in a) gsub(k,a[k]);print} . A construção for(k in a) cria um loop, muito parecido com shells do tipo Bourne em for i in this that other; do something with $i; done , exceto que aqui os valores de k são os subscritos da matriz a . Para cada valor, ele executa gsub (substituto global), que encontra todas as correspondências de uma determinada expressão regular e as substitui por uma determinada string; Eu escolhi os subscritos e conteúdos na matriz (acima) para que, por exemplo, \[3\] seja uma expressão regular que corresponda à cadeia de texto [3] e [source-three] seja a cadeia de texto que você deseja substituir para cada correspondência. gsub opera no registro atual $0 por padrão. Depois de fazer essa substituição para todos os valores em a , ele executa print , o que, por padrão, gera $0 como está agora, com todas as substituições desejadas concluídas.

Nota: GNU awk (gawk), que é comum especialmente no Linux, mas não universal, possui uma otimização onde ele não faz realmente a divisão de campos se nada nos padrões ou ações executadas precisar dos valores do campo. Em outras implementações, uma pequena quantidade de tempo de CPU pode ser desperdiçada, o que o método perl do cuonglm evita, mas a menos que seus arquivos sejam gigantescos, isso provavelmente nem será perceptível.

    
por 22.09.2016 / 13:41
2

Você deve usar perl :

$ perl -ne '
  ++$nr;
  if ($nr == $.) {
    @w = split;
    $k{$w[0]} = $w[1];
  }
  else {
    for $i (keys %k) {
      s/(\[)$i(\])/$1.$k{$i}.$2/ge
    }
    print;
  }
  close ARGV if eof;
' key.txt essay.txt
Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three]
    
por 22.09.2016 / 06:31
0
bash$ sed -f  <( sed -rn 's#([0-9]+)\s+(.*)#s/\[]/[]/g#p' key.txt ) essay.txt

Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three].
    
por 22.01.2017 / 22:10