%20com%20base%20em%20uma%20lista%3F%20Preciso%20trocar%20v%C3%A1rias%20palavras%2C%20por%20outras%20palavras%20correspondentes.png)
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 preciso expandir em palavras, um exemplo prático é trocar as citações numeradas de um ensaio típico para o formato MLA:
essay.txt
:
Sentence 1 [1]. sentence two [1][2]. Sentence three[1][3].
Key.txt
(este é um arquivo delimitado por tabulações):
1 source-one
2 source-two
3 source-three
...etc
Esperado Result.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 entendo o suficiente sed
ou tr
não faço direito:
cat essay.txt | sed s/$(awk {print $1} key.txt)/$(awk {print $2} key.txt)/g
PS: Se houver um truque no notepad ++ para localizar e substituir em massa usando vários termos, isso seria ótimo. Do jeito que está, parece que localizar e substituir funciona apenas para um termo de cada vez, mas preciso encontrar uma maneira de fazer isso em massa para muitos termos ao mesmo tempo.
Responder1
Você deve usar perl
em vez disso:
$ 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]
Responder2
awk
pode fazer efetivamente o mesmo que perl
aquium pouco mais simples, embora implementações diferentes do GNU possam desperdiçar um pouco de tempo de CPU dividindo desnecessariamente o arquivo de texto (grande?):
awk 'NR==FNR{a["\\["$1"\\]"]="["$2"]";next} {for(k in a) gsub(k,a[k]);print}' key.txt essay.txt
Desde que você pediuexplicação:
awk
opera pegando um 'script' que consiste em pares padrão-ação, depois 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 (que inclui tabulação) e aplica o script por sua vez (a menos que seja orientado de outra forma) testando cada padrão (que geralmente olha para o registro atual e/ou seus campos) e se ele corresponde à execução da ação (que geralmente faz algo para ou com o referido registro e/ou campos). Aqui eu especifico dois arquivoskey.txt essay.txt
para que ele leia esses dois arquivos nessa ordem, linha por linha. O roteiropodeser colocado em um arquivo em vez de na linha de comando, mas aqui optei por não fazê-lo.o primeiro padrão é
NR==FNR
.NR
é uma variável interna que é o número do registro que está sendo processado;FNR
é da mesma forma o número do registro no arquivo de entrada atual. Para o primeiro arquivo (key.txt
) estes são iguais; para o segundo arquivo (e quaisquer outros) eles são desiguaisa primeira ação é
{a["\\["$1"\\]"]="["$2"]";next}
.awk
possui matrizes 'associativas' ou 'hashed';arrayname[subexpr]
ondesubexpr
é uma expressão com valor de string que lê ou define um elemento da matriz.$number
por exemplo$1 $2
, etc faz referência aos campos e$0
faz referência a todo o registro. Conforme acima, esta ação é executada apenas para linhas in,key.txt
por exemplo, na última linha desse arquivo$1
is3
e$2
issource-three
, e isso armazena uma entrada de array com um subscrito\[3\]
e um conteúdo de[source-three]
; veja abaixo por que escolhi esses valores. Os"\\["
e"\\]"
são literais de string usando escapes cujos valores reais são\[
e\]
while"[" "]"
são apenas[ ]
, e operandos de string sem operador entre eles são concatenados. Finalmente esta ação é executada,next
o que significa pular o resto do script para este registro, apenas voltar ao topo do loop e iniciar no próximo registro.o segundo padrão está vazio, portanto corresponde a todas as linhas do segundo arquivo e executa a ação
{for(k in a) gsub(k,a[k]);print}
. Afor(k in a)
construção cria um loop, bem como os shells do tipo Bourne fazem emfor i in this that other; do something with $i; done
, exceto que aqui os valores dek
são ossubscritosda matriza
. Para cada valor, ele executagsub
(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 o conteúdo da matriz (acima) para que, por exemplo,\[3\]
seja uma expressão regular que corresponda à string de texto[3]
e[source-three]
seja a string 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,a
ele executaprint
o que, por padrão, é exibido$0
como está agora, com todas as substituições desejadas feitas.
Nota: GNU awk (gawk), que é comum especialmente no Linux, mas não universal, tem uma otimização onde na verdade não faz a divisão de campos se nada nos padrões ou ações executadas precisar dos valores dos campos. Em outras implementações, uma pequena quantidade de tempo de CPU pode ser desperdiçada, o que o método do cuonglm perl
evita, mas a menos que seus arquivos sejam enormes, isso provavelmente nem será perceptível.
Responder3
bash$ sed -f <( sed -rn 's#([0-9]+)\s+(.*)#s/\\[\1]/[\2]/g#p' key.txt ) essay.txt
Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three].
Responder4
Você pode usar a substituição sed no local dentro de um loop para conseguir isso:
$ cp essay.txt Result.txt
$ while read n k; do sed -i "s/\[$n\]/\[$k\]/g" Result.txt; done < key.txt
$ cat Result.txt
Sentence 1 [source-one]. sentence two [source-one][source-two]. Sentence three[source-one][source-three].