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.csvarchivo con formato id\tfoo\tbar...y un ids.csvarchivo con la lista de identificadores, solo seleccione los registros table.csvcon 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 -Fobviamente produce falsos positivos si los identificadores son de ancho variable.
joines 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.
awkes 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 -fes 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.sortPuede hacer todo tipo de clasificación, numérica, alfabética y otras. Verman sort.Respuestas:
Supongo que querías decir que
grep -fno,grep -Fpero 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.
-welimina este problema y-Fse 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
perlembargo , personalmente haría esto en :fuente
^con -F, no puede apuntar específicamente a la primera columna.^id\tbit del OP implica queidpodría ocurrir en otra columna. Si no, esto no importa.La
joinutilidad 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
joinno 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.awksolució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 sortyman join).Como ejemplo más general, suponga que está uniendo dos archivos separados por comas:
input1.csven la tercera columna yinput2.csven 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.csvAquí las opciones
-1y-2especifican en qué campos unirse en los archivos de entrada primero y segundo respectivamente.fuente
También puedes usar ruby para hacer algo similar:
fuente