¿Cómo leer un archivo de entrada de cadenas, hacer coincidir y cambiar las coincidencias en el lugar?

¿Cómo leer un archivo de entrada de cadenas, hacer coincidir y cambiar las coincidencias en el lugar?

Tengo un archivo de texto con cadenas/nombres de archivo en líneas separadas, por ejemplo. filename.txt. Hay cientos de nombres de archivos.

ABC123_S386_R1_001
JKL345_S441_R1_001
filename9000_S587_R1_001

y otro archivo de texto con la cadena/nombres de archivo y datos adicionales, por ejemplo. results.txt:

>ABC123_S386_R1_001 
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>JKL345_S441_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>abc7890_S387_R1_001  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>filename9000_S587_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

Ahora no todos los nombres de archivos filename.txtestán presentes results.txtni están en orden. Quiero insertar un prefijo a todos los nombres de archivos desde filename.txthasta results.txtpero no a los demás.

¿Cómo leo un archivo de entrada de cadenas, lo comparo con otro archivo y cambio las coincidencias?

Anteriormente solía hacer coincidir nombres de archivos individuales con sequence.txt, obtener su número de línea y usarlos sedcon números de línea para cambiar una sola línea o un bloque de líneas.

Mi resultado deseado se vería así

>h-19/US/CA-ABC123_S386_R1_001 
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>h-19/US/CA-JKL345_S441_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>abc7890_S387_R1_001  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>h-19/US/CA-filename9000_S587_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

¿Dónde h-19/US/CA-está el sufijo que me gustaría agregar a todas las coincidencias?

Editar: >es el primer carácter de todas las cadenas que deben cambiarse, no hay caracteres antes >ni espacios en blanco al final del nombre del archivo.

Respuesta1

Suponiendo que las líneas relevantes results.txtno contienen espacios en blanco después del nombre del archivo, el siguiente awkprograma funcionará:

awk -v prefix="h-19/US/CA-" 'NR==FNR{fnames[$1]; next} \
    /^>/{name=substr($0,2); if (name in fnames) {sub(/^>/, ">" prefix)} }1' filenames.txt results.txt
  • Esto primero analizará filenames.txty luego results.txt.
  • Mientras analiza filenames.txt(donde FNR, el contador de líneas por archivo, es igual al NRcontador de líneas global), registrará todos los nombres de archivos (que son los únicos campos en la línea) en una matriz fnames, pero luego saltará la ejecución inmediatamente a la siguiente línea.
  • Mientras analiza, results.txtcomprobará si una línea comienza con >. Si es así, comprobará si la subcadena que sigue a ese carácter (almacenada temporalmente en name) se encuentra entre los "índices de matriz" de fnames. Si ese es el caso, se utilizará sub()para sustituir el inicio >con >+el prefijo, pasado awkcomo variable prefix(a través de la -vdirectiva).
  • El aparentemente "extraviado" 1le indicará awkque imprima la línea actual, incluidas todas las modificaciones posibles (pero sólo porque results.txtdurante el procesamiento del primer archivo no llegamos a esa parte).

Tenga en cuenta que awkpor sí solo no puede modificar archivos in situ, por lo que necesitará trabajar con un archivo temporal. Si tiene una versión suficientemente nueva de GNU Awk (> 4.1.0), puede usar la inplaceextensión; Por supuesto, tendrás que desactivar la opción para el filenames.txtarchivo:

awk -i inplace -v prefix=" ... " ' ... ' inplace=0 filenames.txt inplace=1 results.txt

Esto activará filenames.txty desactivará la edición in situ durante results.txt.

Respuesta2

Con sedél puede recopilar los nombres de archivos en el espacio de espera y luego results.txtverificar todas las líneas en busca de coincidencias para filtrar qué líneas cambiar:

sed -e '1,/^$/{H;1h;d;}' -e 'G;/^>\(.*\).*\n\1\n/s_^>_>h-19/US/CA-_;P;d' filename.txt <((echo)) results.txt
  • Verá que paso una línea vacía entre <((echo))los archivos, por lo que 1,/^$/aborda todas las líneas del primer archivo (y la línea vacía)
  • Esas líneas se agregan al espacio de retención y luego se eliminan H;1h;d( 1hevita comenzar el espacio de retención con una nueva línea)
  • Gagrega el espacio de retención a todas las líneas result.txty /^>\(.*\).*\n\1\n/hace coincidir aquellas líneas que comienzan con >una cadena que es un nombre de archivo (entre líneas nuevas en el espacio de retención)
  • s_^>_>h-19/US/CA-_hace el reemplazo para esas líneas
  • P;dimprime solo la primera línea sin la basura adjunta. Podrías hacerlo s/\n.*//en su lugar

Respuesta3

Úselo perlpara ediciones in situ en el archivo de entrada:

pfx='h-19/US/CA-' \
perl -pi -e '
  BEGIN { %h = map { tr/\n//dr => $ENV{pfx}} <STDIN>}
  s/^>\K(?=(.*))/$h{$1}/;
' results.txt < filename.txt

información relacionada