Поиск в текстовом файле по столбцу

Поиск в текстовом файле по столбцу

У меня есть файл в таком формате:

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

и я хотел бы иметь возможность делать следующее с помощью инструментов командной строки:

my_command "Ideas worth zero"

и получаем такой результат:

42

и не рисковать получить такой результат:

12

Я думал использовать grep для идентификации строки и awk для получения первого поля, но я не уверен, как надежно и эффективно выполнить сопоставление по всему полю «NAME», не считая подсчета столбцов, в которых в заголовке появляются тексты «OWNER_NAME» и «SIZE», и получения всего, что находится между ними, с некоторой обрезкой пробелов.

Обратите внимание, что «OWNER_NAME» может состоять из нескольких слов: например, «OWNER_NAME» = «Я — Бэтмен».

Есть ли идеи по их реализации?

Здесь мне придется руководствоваться только старым семейством команд: cat, head, tail, awk, sed, grep, cut и т. д.

решение1

Хорошо, если длина столбцов неизвестна, я бы перешел на более мощный язык, чем 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";
    }
}

решение2

Если ширина полей постоянна, т.е. формат файла, который вы показали, с максимальной шириной полей, вы можете использовать GNU awk ( gawk(1)) и задать FIELDWIDTHSпеременную для использования анализа с фиксированной шириной:

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

Вы можете обернуть это в скрипт оболочки или функцию и параметризовать searchstr( -v searchstr="$1").

Однако, если поля имеют переменную ширину, то есть если данные изменяются, ширина полей может измениться, вам нужно будет быть немного умнее и динамически определять ширину полей, просматривая первую строку. Учитывая, что одно поле называется OWNER_NAME, используя подчеркивание, я предполагаю, что пробелы в именах полей отсутствуют, поэтому я могу предположить, что пробелы разделяют имена полей.

Определив это, вы можете заменить BEGIN...строку следующим кодом:

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
}

Это позволит просмотреть поля в первой строке и вычислить ширину полей, вычислив разницу между позициями последующих полей для второго и последнего поля. Я предположил, что ширина последнего поля равна 5, но я думаю, что вы можете просто поставить там большое число, и это будет работать с тем, что осталось.

Нам нужно найти пробел до и после имени, чтобы убедиться, что мы не найдем его NAMEвнутри OWNER_NAME(или если там было поле с именем OWNER), а вместо этого сопоставить все поле (нам также нужно добавить пробел, чтобы $0убедиться, что мы можем сопоставить пробел в конце, даже если там его нет).

Вы могли бы пойти более хитрым путем и делать запросы по имени поля, а не только по $3, но я оставлю это на ваше усмотрение.

решение3

Вероятно, проще всего сначала отфильтровать строки по критерию «Идеи, стоящие ноль», а затем отбросить строки «... или больше»:

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

И чтобы получить число из этой трубы, введите:

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

Что вырезает первое поле (разделенное пробелом) и удаляет квадратные скобки.

Лучше всего будет немного изменить формат файла таким образом, чтобы он содержал правильные разделители полей.

решение4

Это может вам помочь:

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

Он вырезает из входных данных только соответствующий столбец, ищет номер строки, в которой появляется строка, затем берет эту строку и оставляет из нее только номер в первом столбце.

Связанный контент