Лучший / оптимизированный способ поиска / поиска текста в файле в скриптах BASH - PullRequest
2 голосов
/ 14 марта 2019

У меня есть файл с сотнями тысяч записей.Все эти записи являются уникальными значениями, разделенными запятыми.Первый столбец можно считать ключевым, а второй - интересующим значением.

Размер файла составляет от 8 до 10 МБ.Я должен искать эти значения время от времени в сценарии.В настоящее время я использую приведенный ниже оператор grep:

myvalue = $ (grep $ myvar filename | cut -d, -f2)

Работает нормально, но реальная проблемамногократный / последовательный поиск в одном и том же файле.Я думаю, что это не очень оптимизированный способ, так как мне приходится искать один и тот же файл несколько раз (более 100-200 раз) во время выполнения моего скрипта, поэтому каждый раз он будет отображать весь файл.Я хочу какой-то лучший / оптимизированный способ.

update Важно отметить, что это последовательный скрипт, и все значения в $ myvar генерируются во время выполнения, поэтому я не могу получить все доступные значения и выполнить комбинированный поиск,это должен быть поиск по одному значению в каждой итерации

Ответы [ 3 ]

2 голосов
/ 14 марта 2019

Если файл создается один раз, а затем на него ссылаются снова и снова, без изменения между ними, вам нужно использовать ассоциативный массив в качестве таблицы поиска. Это может стать большим и безобразным в bash; вместо этого рассмотрим perl.

Однако вы спросили, как это сделать в bash.

$: eval "declare -A lookup=(
   $( sed -E 's/^([^,]+),([^,]+).*/  [\1]=\2/' filename )
   )" 

Теперь все значения должны быть в таблице lookup.

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

sed -E 's/^([^,]+),([^,]+).*/  [\1]=\2/'

берет первое и второе поля файла с разделителями-запятыми и переформатирует их в присваивания ключ / значение в синтаксисе bash, например:

declare -A lookup=(
   [a]=1
   [b]=2
   [c]=3 # ... and so on
) 

eval разбирает все это в текущей среде для вашего использования.

Не более grep х. Просто используйте "${lookup[$myvar]}".
Если вы просто хотели назначить его для удобства чтения, тогда вместо grep используйте

myvalue="${lookup[$myvar]}"

Мой локальный пример использования:

$: cat x
a,1,lijhgf
b,2,;lsaoidj
c,3,;l'skd

$: echo "declare -A lookup=(
   $( sed -E 's/^([^,]+),([^,]+).*/  [\1]=\2/' x )
   )"
   declare -A lookup=(
     [a]=1
     [b]=2
     [c]=3
   )

$: eval "declare -A lookup=(
   $( sed -E 's/^([^,]+),([^,]+),.*/  [\1]=\2/' x )
   )"

$: echo "${lookup[b]}"
   2
2 голосов
/ 14 марта 2019

Одна из очевидных вещей, о которых я думаю, это ограничение grep результатов, что можно сделать с помощью переключателя -m:

Prompt>cat test.txt
a
a
b
a
b

Prompt>grep "a" test.txt
a
a
a

Prompt>grep -m 1 "a" test.txt
a
1 голос
/ 14 марта 2019

Прежде всего, давайте посмотрим на вашу команду:

myvalue=$(grep $myvar filename | cut -d, -f2)

Вы используете 2 двоичных файла, которые вы загружаете (grep и cut) для обработки данных. Вы должны попытаться уменьшить это до одного двоичного файла. Это уже очень поможет:

myvalue=$(awk -F, -v var="$myvar" '$0~var { print $2; exit}' filename)

Это будет намного быстрее, чем:

  • это отдельная библиотека
  • прекращает чтение файла с момента обнаружения записи

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

 while IFS= read -r; do
    declare -A z+="( $REPLY )"
 done < <(awk -F, '{print "["$1"]="$0}' lookupfile)

 echo ${z[$key]}

на основе Как заполнить ассоциативный массив bash выводом команды?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...