Búsqueda de archivos de texto por columna

Búsqueda de archivos de texto por columna

Tengo un archivo en este formato:

[#]   OWNER_NAME     NAME                       SIZE
[6]   Robottinosino  Software                   200
[42]  Robottinosino  Ideas worth zero           188
[12]  Robottinosino  Ideas worth zero or more   111
[13]  I am Batman    Hardware                   180
[25]  Robottinosino  Profile Pictures           170

y me gustaría poder hacer lo siguiente usando herramientas de línea de comando:

my_command "Ideas worth zero"

y obtener este resultado:

42

y no correr el riesgo de obtener este resultado:

12

He pensado en usar grep para identificar la línea, awk para obtener el primer campo, pero no estoy seguro de cómo hacer coincidir de manera confiable y eficiente todo el campo 'NOMBRE' sin contar en qué columna está el texto 'PROPIETARIO_NOMBRE' y 'TAMAÑO'. Aparece en el encabezado y obtiene todo lo que se encuentra en el medio con algunos recortes de espacios en blanco.

Tenga en cuenta que 'PROPIETARIO_NOMBRE' puede contener más de una palabra: por ejemplo, 'PROPIETARIO_NOMBRE' = "Soy Batman".

¿Alguna idea con la implementación que la acompaña?

Lo que tengo que seguir aquí es solo la vieja familia de cat, head, tail, awk, sed, grep, cut, etc.

Respuesta1

Bien, si no se conoce la longitud de las columnas, cambiaría a un lenguaje más potente que bash:

#!/usr/bin/perl
use warnings;
use strict;

my $string = shift;
open my $FH, '<', '1.txt' or die $!;
my $first_line = <$FH>;
my ($before, $name) = $first_line =~ /(.* )(NAME *)/;
my $column = length $before;
$string .= ' ' x (length($name) - length $string);     # adjust the length of $string
while (<$FH>) {
    if ($column == index $_, $string, $column) {
        /^\[([0-9]+)\]/ and print "$1\n";
    }
}

Respuesta2

Si los anchos de campo son constantes, es decir, el formato de archivo que ha mostrado con los anchos de campo que tiene son máximos, puede usar GNU awk ( gawk(1)) y configurar la FIELDWIDTHSvariable para usar análisis de ancho fijo:

gawk -v searchstr="Ideas worth zero" -- '
    BEGIN { FIELDWIDTHS="6 15 27 5" }  # assuming the final field width is 5
    # Pre-process data
    {
        gsub(/[^[:digit:]]/, "", $1)  # strip out non-numbers
        for (i = 2; i <= NF; i++)
            gsub(/[[:space:]]*$/, "", $i)  # strip trailing whitespace
    }
    # match here
    $3 == searchstr { print $1 }
' file.txt

Puede envolver eso en un script de shell o una función y parametrizar searchstr( -v searchstr="$1").

Sin embargo, si los campos son de ancho variable (es decir, si los datos cambian, el ancho de los campos puede cambiar), necesitará ser un poco más inteligente y determinar dinámicamente los anchos de los campos inspeccionando la primera línea. Dado que un campo se llama OWNER_NAME, usando un guión bajo, supongo que no hay espacios en los nombres de los campos, por lo que puedo suponer que los espacios en blanco separan los nombres de los campos.

Con eso definido, puedes reemplazar la BEGIN...línea con este código:

NR == 1 {
    for (i = 2; i <= NF; i++)
        FIELDWIDTHS=FIELDWIDTHS index($0" ", " "$i" ")-index($0" ", " "$(i-1)" ") " "
    FIELDWIDTHS=FIELDWIDTHS "5"  # assuming 5 is the width of the last field
    next
}

Eso mirará los campos en la primera línea y calculará los anchos de los campos calculando la diferencia entre las posiciones de los campos posteriores desde el penúltimo hasta el último campo. Supuse que el ancho del último campo es 5, pero creo que puedes poner un número grande allí y funcionará con lo que sobra.

Necesitamos buscar un espacio antes y después del nombre para asegurarnos de no encontrar NAMEel interior OWNER_NAME(o si había un campo llamado OWNER) y, en su lugar, hacer coincidir todo el campo (también debemos agregar un espacio para $0asegurarnos de que podemos hacer coincidir un espacio al final incluso si no hubiera ninguno allí).

Podría ser más sofisticado para poder consultar por nombre de campo en lugar de hacer coincidir solo con $3, pero eso te lo dejo a ti.

Respuesta3

Probablemente lo más sencillo es filtrar las líneas primero por 'Ideas que valen cero y luego tirar las líneas'... o más':

grep 'Ideas worth zero' | grep -v 'Ideas worth zero or more'

Y para obtener el número de esa tubería, ingrese la entrada a:

cut -d' ' -f1 | tr -d ']['

Lo cual corta el primer campo (delimitado por un espacio) y eliminando los corchetes.

Lo mejor sería si pudiera cambiar ligeramente el formato del archivo de tal manera que venga con delimitadores de campo adecuados.

Respuesta4

Esto puede ayudarte:

function my_command () {
    sed -n $(cut -b22-48 1.txt |
        grep -n "$1"' *$' |
        cut -f1 -d: )p 1.txt \
            | cut -d' ' -f1 | tr -d ']['
}

Corta solo la columna relevante de la entrada, busca el número de línea donde aparece la cadena, luego toma esta línea y solo conserva el número de la primera columna.

información relacionada