Utilizo una gran cantidad de grep awk en mi shell de Unix para trabajar con archivos de texto de columna separados por tabulaciones de tamaño mediano (alrededor de 10M-100M líneas). A este respecto, Unix Shell es mi hoja de cálculo.
Pero tengo un gran problema: seleccionar registros con una lista de ID.
Al tener un table.csv
archivo con formato id\tfoo\tbar...
y un ids.csv
archivo con la lista de identificadores, solo seleccione los registros table.csv
con el ID presente ids.csv
.
tipo de /programming/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids pero con shell, no perl.
grep -F
obviamente produce falsos positivos si los identificadores son de ancho variable.
join
es una utilidad que nunca podría entender. En primer lugar, requiere una clasificación alfabética (mis archivos generalmente están ordenados numéricamente), pero aun así no puedo hacer que funcione sin quejarme por un orden incorrecto y omitir algunos registros. Entonces no me gusta. grep -f contra el archivo con ^id\t
-s es muy lento cuando el número de identificadores es grande.
awk
es engorroso
¿Hay alguna buena solución para esto? ¿Alguna herramienta específica para archivos separados por tabulaciones? La funcionalidad adicional también será bienvenida.
UPD: corregido sort
->join
grep -f
es demasiado lento, mantener esta estrategia parece más problemas de lo que vale: las variaciones probablemente serán víctimas de los mismos problemas de rendimiento O (N * M). Tal vez su tiempo se gastaría mejor aprendiendo cómo usar una base de datos SQL normalizada ...awk
.sort
Puede hacer todo tipo de clasificación, numérica, alfabética y otras. Verman sort
.Respuestas:
Supongo que querías decir que
grep -f
no,grep -F
pero en realidad necesitas una combinación de ambos y-w
:La razón por la que estaba obteniendo falsos positivos es (supongo que no lo explicó) porque si una identificación puede estar contenida en otra, entonces ambas se imprimirán.
-w
elimina este problema y-F
se asegura de que sus patrones se traten como cadenas, no como expresiones regulares. Deman grep
:Si sus falsos positivos se deben a que una ID puede estar presente en un campo que no es ID, recorra su archivo en su lugar:
o, más rápido:
Sin
perl
embargo , personalmente haría esto en :fuente
^
con -F, no puede apuntar específicamente a la primera columna.^id\t
bit del OP implica queid
podría ocurrir en otra columna. Si no, esto no importa.La
join
utilidad es lo que quieres. Requiere que los archivos de entrada estén ordenados léxicamente.Asumiendo que su shell es bash o ksh:
Sin necesidad de ordenar, la solución awk habitual es
fuente
join
no es un error: tus palabras fueron las que no pudiste resolver. Abre tu mente y aprende. ¿Qué resultado obtuviste y cómo difiere eso de lo que esperas?join
.awk
solución aquí es muy rápida y eficiente para mis propósitos (estoy extrayendo subconjuntos de unos cientos de archivos con ~ 100 millones de líneas)Las respuestas a esta pregunta SO me ayudaron a superar los inconvenientes con join. Esencialmente, cuando ordena el archivo en preparación para enviarlo a unirse, debe asegurarse de que está ordenando según la columna a la que se está uniendo. Entonces, si ese es el primero, debe decirle cuál es el carácter separador en el archivo y que desea que se ordene en el primer campo (y solo en el primer campo). De lo contrario, si el primer campo tiene anchos variables (por ejemplo), sus separadores y posiblemente otros campos pueden comenzar a afectar el orden de clasificación.
Por lo tanto, use la opción -t de clasificación para especificar su carácter de separación, y use la opción -k para especificar el campo (recordando que necesita un campo inicial y final, incluso si es el mismo) o se ordenará a partir de ese carácter hasta el final de la línea).
Entonces, para un archivo separado por tabulaciones como en esta pregunta, lo siguiente debería funcionar (gracias a la respuesta de Glenn para la estructura):
join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv
(Como referencia, el indicador -d significa clasificación del diccionario. También puede utilizar el indicador -b para ignorar los espacios en blanco iniciales, ver
man sort
yman join
).Como ejemplo más general, suponga que está uniendo dos archivos separados por comas:
input1.csv
en la tercera columna yinput2.csv
en la cuarta. Podrías usarjoin -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv
Aquí las opciones
-1
y-2
especifican en qué campos unirse en los archivos de entrada primero y segundo respectivamente.fuente
También puedes usar ruby para hacer algo similar:
fuente