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 FIELDWIDTHS
Variable 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_NAME
einem 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 NAME
darin 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 $0
sicherzustellen, 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.