¿Cómo hacer una sustitución de sed (s///g) basada en una lista? Necesito intercambiar varias palabras con otras palabras correspondientes

¿Cómo hacer una sustitución de sed (s///g) basada en una lista? Necesito intercambiar varias palabras con otras palabras correspondientes

No creo que esta pregunta se haya hecho antes, así que no sé si sedes 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 sedni trpara 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 perlen 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

awkPuedo hacer efectivamente lo mismo que perlaquí.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:

  • awkopera 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 archivos key.txt essay.txtpara 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. NRes una variable incorporada que es el número del registro que se está procesando; FNRes 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 desiguales

  • la primera acción es {a["\\["$1"\\]"]="["$2"]";next}. awktiene matrices 'asociativas' o 'hash'; arrayname[subexpr]donde subexpruna expresión con valores de cadena lee o establece un elemento de la matriz. $numberpor ejemplo $1 $2, etc. hacen referencia a los campos y $0hacen referencia a todo el registro. Según lo anterior, esta acción se ejecuta solo para las líneas, key.txtpor ejemplo, en la última línea de ese archivo $1es 3y $2es source-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, nextlo 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}. La for(k in a)construcción crea un bucle, muy parecido a lo que hacen los shells tipo Bourne en for i in this that other; do something with $i; done, excepto que aquí los valores de kson lossubíndicesde la matriz a. Para cada uno de estos valores, ejecuta gsub(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. gsubopera en el registro actual $0de forma predeterminada. Después de realizar esta sustitución de todos los valores que contiene, ase ejecuta print, lo que de forma predeterminada $0genera 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].

información relacionada