%20basada%20en%20una%20lista%3F%20Necesito%20intercambiar%20varias%20palabras%20con%20otras%20palabras%20correspondientes.png)
No creo que esta pregunta se haya hecho antes, así que no sé si sed
es capaz de hacerlo.
Supongamos que tengo un montón de números en una oración que necesito expandir en palabras, un ejemplo práctico es intercambiar las citas numeradas en un ensayo típico al formato MLA:
essay.txt
:
Sentence 1 [1]. sentence two [1][2]. Sentence three[1][3].
Key.txt
(este es un archivo delimitado por tabulaciones):
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]
Aquí está mi intento de pseudocódigo, pero no entiendo lo suficiente sed
ni tr
para hacerlo bien:
cat essay.txt | sed s/$(awk {print $1} key.txt)/$(awk {print $2} key.txt)/g
PD: Si hay un truco en notepad++ para buscar y reemplazar masivamente usando varios términos, sería genial. Tal como están las cosas, parece que buscar y reemplazar solo funciona para un término a la vez, pero necesito una manera de hacerlo en masa para muchos términos a la vez.
Respuesta1
Deberías usar perl
en su lugar:
$ 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]
Respuesta2
awk
Puedo hacer efectivamente lo mismo que perl
aquí.un poco mas simple, aunque implementaciones distintas a GNU pueden perder un poco de tiempo de CPU dividiendo innecesariamente el archivo 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 pedisteexplicación:
awk
opera tomando un 'script' que consta de pares patrón-acción, luego lee uno o más archivos (o entrada estándar) un 'registro' a la vez donde por defecto cada registro es una línea, y para cada registro lo divide en campos por predeterminado en el espacio en blanco (que incluye la pestaña) y aplica el script por turnos (a menos que se indique lo contrario) probando cada patrón (que a menudo mira el registro actual y/o sus campos) y si coincide ejecutando la acción (que a menudo hace algo para o con dicho registro y/o campos). Aquí especifico dos archivoskey.txt essay.txt
para que los lea en ese orden, línea por línea. La secuencia de comandospodercolocarse en un archivo en lugar de en la línea de comando, pero aquí elegí no hacerlo.el primer patrón es
NR==FNR
.NR
es una variable incorporada que es el número del registro que se está procesando;FNR
es de manera similar el número del registro dentro del archivo de entrada actual. Para el primer archivo (key.txt
) estos son iguales; para el segundo archivo (y cualquier otro) son desigualesla primera acción es
{a["\\["$1"\\]"]="["$2"]";next}
.awk
tiene matrices 'asociativas' o 'hash';arrayname[subexpr]
dondesubexpr
una expresión con valores de cadena lee o establece un elemento de la matriz.$number
por ejemplo$1 $2
, etc. hacen referencia a los campos y$0
hacen referencia a todo el registro. Según lo anterior, esta acción se ejecuta solo para las líneas,key.txt
por ejemplo, en la última línea de ese archivo$1
es3
y$2
essource-three
, y esto almacena una entrada de matriz con un subíndice de\[3\]
y un contenido de[source-three]
; Vea a continuación por qué elegí estos valores. Los"\\["
and"\\]"
son literales de cadena que utilizan escapes cuyos valores reales son\[
y\]
while"[" "]"
son just[ ]
y los operandos de cadena sin ningún operador entre ellos se concatenan. Finalmente se ejecuta esta acción,next
lo que significa omitir el resto del script para este registro, simplemente regresar a la parte superior del bucle y comenzar en el siguiente registro.el segundo patrón está vacío, por lo que coincide con cada línea del segundo archivo y ejecuta la acción
{for(k in a) gsub(k,a[k]);print}
. Lafor(k in a)
construcción crea un bucle, muy parecido a lo que hacen los shells tipo Bourne enfor i in this that other; do something with $i; done
, excepto que aquí los valores dek
son lossubíndicesde la matriza
. Para cada uno de estos valores, ejecutagsub
(sustituto global) que encuentra todas las coincidencias de una expresión regular determinada y las reemplaza con una cadena determinada; Elegí los subíndices y el contenido de la matriz (arriba) para que, por ejemplo,\[3\]
sea una expresión regular que coincida con la cadena de texto[3]
y[source-three]
sea la cadena de texto que desea sustituir en cada una de esas coincidencias.gsub
opera en el registro actual$0
de forma predeterminada. Después de realizar esta sustitución de todos los valores que contiene,a
se ejecutaprint
, lo que de forma predeterminada$0
genera el resultado actual, con todas las sustituciones deseadas realizadas.
Nota: GNU awk (gawk), que es común especialmente en Linux pero no universal, tiene una optimización en la que en realidad no divide los campos si nada en los patrones o acciones ejecutadas necesita los valores de los campos. En otras implementaciones se puede desperdiciar una pequeña cantidad de tiempo de CPU, lo que evita el método de cuonglm perl
, pero a menos que sus archivos sean enormes, es probable que esto ni siquiera se note.
Respuesta3
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].
Respuesta4
Puedes usar la sustitución sed in situ dentro de un bucle para lograr esto:
$ 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].