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.txt
están presentes results.txt
ni están en orden. Quiero insertar un prefijo a todos los nombres de archivos desde filename.txt
hasta results.txt
pero 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 sed
con 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.txt
no contienen espacios en blanco después del nombre del archivo, el siguiente awk
programa 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.txt
y luegoresults.txt
. - Mientras analiza
filenames.txt
(dondeFNR
, el contador de líneas por archivo, es igual alNR
contador de líneas global), registrará todos los nombres de archivos (que son los únicos campos en la línea) en una matrizfnames
, pero luego saltará la ejecución inmediatamente a la siguiente línea. - Mientras analiza,
results.txt
comprobará si una línea comienza con>
. Si es así, comprobará si la subcadena que sigue a ese carácter (almacenada temporalmente enname
) se encuentra entre los "índices de matriz" defnames
. Si ese es el caso, se utilizarásub()
para sustituir el inicio>
con>
+el prefijo, pasadoawk
como variableprefix
(a través de la-v
directiva). - El aparentemente "extraviado"
1
le indicaráawk
que imprima la línea actual, incluidas todas las modificaciones posibles (pero sólo porqueresults.txt
durante el procesamiento del primer archivo no llegamos a esa parte).
Tenga en cuenta que awk
por 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 inplace
extensión; Por supuesto, tendrás que desactivar la opción para el filenames.txt
archivo:
awk -i inplace -v prefix=" ... " ' ... ' inplace=0 filenames.txt inplace=1 results.txt
Esto activará filenames.txt
y 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.txt
verificar 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 que1,/^$/
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
(1h
evita comenzar el espacio de retención con una nueva línea) G
agrega el espacio de retención a todas las líneasresult.txt
y/^>\(.*\).*\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íneasP;d
imprime solo la primera línea sin la basura adjunta. Podrías hacerlos/\n.*//
en su lugar
Respuesta3
Úselo perl
para 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