Textdateisuche nach Spalte

Textdateisuche nach Spalte

Ich habe eine Datei in diesem Format:

[#]   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

und ich möchte mithilfe von Befehlszeilentools Folgendes tun können:

my_command "Ideas worth zero"

und erhalte dieses Ergebnis:

42

und nicht das Risiko eingehen, dieses Ergebnis zu erhalten:

12

Ich habe darüber nachgedacht, grep zum Identifizieren der Zeile und awk zum Abrufen des ersten Felds zu verwenden, bin mir jedoch nicht sicher, wie ich eine zuverlässige und effiziente Übereinstimmung für das gesamte Feld „NAME“ erzielen kann, ohne zu zählen, in welcher Spalte der Text „OWNER_NAME“ und „SIZE“ in der Kopfzeile erscheinen, und alles dazwischen abzurufen und dabei einige Leerzeichen zu entfernen.

Beachten Sie, dass „OWNER_NAME“ aus mehr als einem Wort bestehen kann: z. B. „OWNER_NAME“ = „Ich bin Batman“.

Irgendwelche Ideen mit entsprechender Umsetzung?

Ich muss mich hier nur an die alte Familie von Cat, Head, Tail, Awk, Sed, Grep, Cut usw. halten.

Antwort1

OK, wenn die Länge der Spalten nicht bekannt ist, würde ich auf eine leistungsfähigere Sprache als Bash umsteigen:

#!/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";
    }
}

Antwort2

Wenn die Feldbreiten konstant sind – d. h. das von Ihnen angezeigte Dateiformat mit den vorhandenen Feldbreiten das Maximum erreicht hat – können Sie GNU awk ( gawk(1)) verwenden und die FIELDWIDTHSVariable so einstellen, dass die Analyse mit fester Breite verwendet wird:

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

Sie können das in ein Shell-Skript oder eine Funktion einbinden und parametrisieren searchstr( -v searchstr="$1").

Wenn die Felder jedoch eine variable Breite haben, d. h. wenn sich die Daten ändern, kann sich die Breite der Felder ändern, müssen Sie etwas raffinierter vorgehen und die Feldbreiten dynamisch durch Überprüfen der ersten Zeile ermitteln. Da ein Feld mit OWNER_NAMEeinem Unterstrich aufgerufen wird, gehe ich davon aus, dass in Feldnamen keine Leerzeichen vorhanden sind. Daher kann ich davon ausgehen, dass die Feldnamen durch Leerzeichen getrennt sind.

Wenn dies definiert ist, können Sie die BEGIN...Zeile durch diesen Code ersetzen:

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
}

Dabei werden die Felder in der ersten Zeile betrachtet und die Feldbreiten berechnet, indem die Differenz zwischen den Positionen nachfolgender Felder für das vorletzte Feld berechnet wird. Ich bin davon ausgegangen, dass die Breite des letzten Felds 5 beträgt, aber ich denke, Sie können dort einfach eine große Zahl eingeben und es wird mit dem Rest funktionieren.

Wir müssen vor und nach dem Namen nach einem Leerzeichen suchen, um sicherzustellen, dass wir NAMEdarin nichts finden OWNER_NAME(oder ob es ein Feld mit dem Namen gab OWNER), und stattdessen eine Übereinstimmung mit dem ganzen Feld finden (wir müssen außerdem ein Leerzeichen anhängen, um $0sicherzustellen, dass wir am Ende eine Übereinstimmung mit einem Leerzeichen finden, auch wenn dort keines war).

Sie könnten es noch raffinierter gestalten, so dass Sie nach Feldnamen abfragen können, anstatt nur auf Übereinstimmungen zu setzen $3, aber das überlasse ich Ihnen.

Antwort3

Am einfachsten ist es wahrscheinlich, die Zeilen zuerst nach „Ideen mit einem Wert von null“ zu filtern und dann die Zeilen „… oder mehr“ wegzuwerfen:

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

Und um die Nummer daraus zu erhalten, leiten Sie die Eingabe weiter in:

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

Dadurch wird das erste Feld (durch ein Leerzeichen getrennt) ausgeschnitten und die eckigen Klammern entfernt.

Am besten wäre es, wenn Sie das Dateiformat leicht so ändern könnten, dass es mit richtigen Feldtrennzeichen ausgestattet ist.

Antwort4

Dies kann Ihnen helfen:

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

Dabei schneidet es aus der Eingabe nur die relevante Spalte aus, sucht die Zeilennummer, in der der String vorkommt, nimmt dann diese Zeile und behält davon nur die Nummer der ersten Spalte bei.

verwandte Informationen