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

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

[#] 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» = «Я Бэтмен».

Любые идеи с сопутствующей реализацией?

То, что мне нужно здесь, это просто старая семья кошки, головы, хвоста, awk, sed, grep, cut и т. Д.

6 Solutions collect form web for “Поиск текстового файла по столбцу”

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

Если ширина поля постоянна, то есть формат файла, который вы указали с шириной поля, у вас есть, – вы можете использовать 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 ), и вместо этого нужно OWNER_NAME все поле (нам также нужно добавить пробел в $0 для обеспечения мы можем сопоставить пространство в конце, даже если его там не было).

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

  $ cat test [#] 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 $ cat test.sh #!/bin/bash - awk -F"\t" '(NR<=1){for(i=1;i<NF;i++) if(toupper("'$1'")==toupper($i)) field=i;} (toupper($field) == toupper("'"$2"'")){print $1}' $ cat test | ./test.sh NAME "Ideas worth zero" [42] 

Я не уверен, что разделителем является вкладка. Но это довольно легко изменить с помощью sed . Например, sed 's/\s\s+/\t/g' выполнит задание.

Также вы можете указать любое другое поле, а не только NAME . Он найдет правильный номер столбца.

Если вам понадобится только третий скрипт, будет намного проще.

пс. Я использовал его в своем собственном проекте, поэтому, похоже, вам нужна еще большая функциональность.

UPD. из-за разделителя не запускается линия смены вкладок

  cat test | sed 's/\s\s\+/\t/g' | ./test.sh NAME "Ideas worth zero" 

Он отлично работает на моем сайте.

Вероятно, самый простой для фильтрации строк сначала «Идеи стоят нуля», а затем бросают строки «… или больше»:

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

И чтобы получить номер из этого канала, введите:

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

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

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

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

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

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

Это не то, что я не пробовал, прежде чем спросить … вот моя попытка … но это выглядит слишком сложно для меня. Не обращайте внимания на логику, которая грациозно обрабатывает грязные файлы, это не было частью вопроса, и в любом случае это не фокус текстового поиска. Просто случается, что файлы, которые я иногда не начинаю с «HEADER», но с некоторым количеством мусора, причем все остальные данные абсолютно прекрасные, всегда.

 #!/bin/bash file_to_scan="${1}" name_to_lookup="${2}" ASSUME_FIRST_LINE_IS_HEADER="false" # Sometimes input files begin with spurious lines FILE_HEADER_REGEX='^\[#\][[:blank:]]+OWNER_NAME[[:blank:]]+NAME[[:blank:]]+SIZE\s*$' FIELD_HEADER_NAME=' NAME' FIELD_HEADER_SIZE=' SIZE' if [ "$ASSUME_FIRST_LINE_IS_HEADER" == "true" ]; then header_line=$(head -n 1 "${file_to_scan}") else header_line="$( grep \ --colour=never \ --extended-regexp \ "${FILE_HEADER_REGEX}" \ "${file_to_scan}" )" fi colstartend=($( printf "${header_line}" \ | \ awk \ -v name="${FIELD_HEADER_NAME}" \ -v size="${FIELD_HEADER_SIZE}" \ '{ print index($0, name)+1; print index($0, size); }' )) sed -E "1,/${FILE_HEADER_REGEX}/d" "${file_to_scan}" \ | \ awk \ -v name_to_lookup="${name_to_lookup}" \ -v colstart="${colstartend[0]}" \ -v offset="$(( ${colstartend[1]} - ${colstartend[0]} ))" \ '{ name_field = substr($0, colstart, offset); sub(/ *$/, "", name_field); if (name_field == name_to_lookup) { print substr($1, 2, length($1)-2) } }' 
  • Очень странное поведение с grep и IFS
  • Shell Scripting: вычислить мощность числа с реальным числом в качестве показателя
  • Перенаправление ввода с использованием команды и параметров
  • Псевдотерминальное сообщение в скрипте
  • Клавиша со стрелкой / Ввод меню
  • В чем разница между сценарием sh и sh <script
  • «Тар» - результат поиска, сохраняющий структуру каталогов
  • Расширение *, #,% - Что они означают?
  • Согласование и сохранение и возврат информации на стандартный ввод
  • Запустить сценарий оболочки из crontab после остановки / запуска / перезапуска сервера
  • Получать значения по блоку в одном файле
  • Linux и Unix - лучшая ОС в мире.