Pesquisa de arquivo de texto por coluna

Pesquisa de arquivo de texto por coluna

Eu tenho um arquivo neste 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

e eu gostaria de poder fazer o seguinte usando ferramentas de linha de comando:

my_command "Ideas worth zero"

e obtenha este resultado:

42

e não correr o risco de obter este resultado:

12

Pensei em usar grep para identificar a linha, awk para obter o primeiro campo, mas não tenho certeza de como corresponder de maneira confiável e eficiente em todo o campo 'NAME', sem contar em qual coluna o texto 'OWNER_NAME' e 'SIZE' apareça no cabeçalho e obtenha tudo o que está no meio com alguns cortes de espaços em branco.

Observe que 'OWNER_NAME' pode ter mais de uma palavra: por exemplo, 'OWNER_NAME' = "Eu sou o Batman".

Alguma idéia com implementação concomitante?

O que tenho que seguir aqui é apenas a velha família de cat, head, tail, awk, sed, grep, cut, etc.

Responder1

OK, se o comprimento das colunas não for conhecido, eu mudaria para uma linguagem mais poderosa que o 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";
    }
}

Responder2

Se as larguras dos campos forem constantes - ou seja, o formato do arquivo que você mostrou com as larguras dos campos que você possui estão no máximo - você pode usar GNU awk ( gawk(1)) e definir a FIELDWIDTHSvariável para usar análise de largura fixa:

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

Você pode agrupar isso em um script de shell ou em uma função e parametrizar searchstr( -v searchstr="$1").

No entanto, se os campos tiverem largura variável - ou seja, se os dados mudarem, a largura dos campos poderá mudar - você precisará ser um pouco mais inteligente e determinar dinamicamente as larguras dos campos inspecionando a primeira linha. Dado que um campo é chamado OWNER_NAME, usando um sublinhado, estou assumindo que os espaços não estão presentes nos nomes dos campos, então posso assumir que os espaços em branco separam os nomes dos campos.

Com isso definido, você pode substituir a BEGIN...linha por 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
}

Isso examinará os campos na primeira linha e calculará as larguras dos campos calculando a diferença entre as posições dos campos subsequentes do segundo ao último campo. Presumi que a largura do último campo fosse 5, mas acho que você pode simplesmente colocar um número grande lá e funcionará com o que sobrou.

Precisamos procurar um espaço antes e depois do nome para garantir que não encontraremos NAMEdentro OWNER_NAME(ou se havia um campo chamado OWNER) e, em vez disso, corresponder o campo inteiro (também precisamos anexar um espaço para $0garantir que possamos corresponder a um espaço no final, mesmo que não houvesse nenhum lá).

Você poderia ser mais sofisticado para poder consultar por nome de campo em vez de corresponder apenas on $3, mas deixarei isso para você.

Responder3

Provavelmente o mais simples é filtrar as linhas primeiro por 'Ideias que valem zero e depois descartar as linhas'... ou mais':

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

E para obter o número desse canal, insira:

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

Que corta o primeiro campo (delimitado por um espaço) e removendo os colchetes.

O melhor seria se você pudesse alterar ligeiramente o formato do arquivo de forma que ele venha com delimitadores de campo adequados.

Responder4

Isso pode ajudá-lo:

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

Ele corta apenas a coluna relevante da entrada, procura o número da linha onde a string aparece, depois pega essa linha e mantém apenas o número da primeira coluna dela.

informação relacionada