Вы можете создать базу данных SQLite и выполнять SQL-выборки из нее, что, вероятно, было бы более чистым в реализации и подготовило бы вас к более переносимой работе в дальнейшем.
Но вот примерная идея. Допустим, у меня есть 2 файла:
$ more index.txt new_vals.txt
::::::::::::::
index.txt
::::::::::::::
1_,2_,4_,5_
::::::::::::::
new_vals.txt
::::::::::::::
5_,2_,1_,4
2_,5_,1_,4
С помощью этой команды мы можем найти соответствие:
$ for i in $(<new_vals.txt); do nums=${i//_,/}; \
grep -oE "[${nums}_,]+" index.txt; done
1_,2_,4_,5_
1_,2_,4_,5_
Это демонстрирует, что мы можем сопоставить каждую строку из new_vals.txt
с существующей строкой в index.txt
.
Основываясь на правке ОП, можно сделать то, что он хочет, используя модификацию вышеописанного подхода.
$ for i in $(<new_vals.txt); do
nums=${i//_,/}
printf "# to check: [%s]" $i
k=$(grep -oE "[${nums}_,]+" index.txt | grep "[[:digit:]]_$")
printf " ==> match: [%s]\n" $k
done
С модифицированной версией тестовых данных:
$ more index.txt new_vals.txt
::::::::::::::
index.txt
::::::::::::::
1_,2_,4_,5_
0_,2_,3_,9_
::::::::::::::
new_vals.txt
::::::::::::::
5_,2_,1_,4_
2_,5_,1_,4_
1_,1_,1_,1_
1_,2_,4_,4_
Теперь, когда мы запускаем вышеописанное (для простоты помещенное в скрипт, parser.bash
):
$ ./parser.bash
# to check: [5_,2_,1_,4_] ==> match: [1_,2_,4_,5_]
# to check: [2_,5_,1_,4_] ==> match: [1_,2_,4_,5_]
# to check: [1_,1_,1_,1_] ==> match: []
# to check: [1_,2_,4_,4_] ==> match: []
Вышеописанный метод работает, используя некоторые ключевые характеристики, проявляющиеся в природе ваших данных. Например. Только совпадения будут заканчиваться цифрой, за которой следует знак подчеркивания. grep "[[:digit:]]_$"
выбирает только эти результаты.
Другая часть скрипта, grep -oE "[${nums}_,]+" index.txt
выберет строки, содержащие символы строк из файла new_vals.txt
, которые соответствуют строкам из index.txt
.
Если природа данных такова, что длина строк может быть разной, то 2-й grep нужно будет расширить, чтобы гарантировать, что мы выбираем только строки достаточной длины. Есть несколько способов добиться этого, либо расширив шаблон, либо используя счетчик, возможно, с помощью wc
или других средств, которые подтвердят, что совпадения определенного типа.
Расширение шаблона следующим образом:
k=$(grep -oE "[${nums}_,]+" index.txt | \
grep "[[:digit:]]_,[[:digit:]]_,[[:digit:]]_,[[:digit:]]_$")
Позволит избавиться от строк типа:
$ ./parser2.bash
# to check: [5_,2_,1_,4_] ==> match: [1_,2_,4_,5_]
# to check: [2_,5_,1_,4_] ==> match: [1_,2_,4_,5_]
# to check: [1_,1_,1_,1_] ==> match: []
# to check: [1_,2_,4_,4_] ==> match: []
# to check: [1_,2_,5_] ==> match: []
OUTPUT:
Не совсем уверен, как правильно это представить в [117774]Markdown[117775], но вывод из вышеприведенной команды на самом деле является новой строкой и единым пробелом, который разделил две строки, затем вышеприведенную, затем еще одну новую строку и пробел.
$ dpkg --get-selections | grep linux-image
linux-image-2.6.35-22-generic deinstall
linux-image-2.6.35-24-generic install
linux-image-2.6.35-25-generic install
linux-image-2.6.35-27-generic install
linux-image-2.6.35-28-generic install
linux-image-generic install
$ sudo aptitude remove linux-image-2.6.35-24-generic linux-image-2.6.35-25-generic
Функция [117776]t
sed[117779] является полностью портативным устройством [117780], определенным POSIX[117781], поэтому:[117099]. [117100] [117782][[117948]2addr[117949]][117950]t[117951] [[117952]label[117953]] Тест. Ответвление на командный глагол [117954]: [117955] с меткой [117956] [117957], если с момента последнего чтения входной строки или выполнения [117958]t[117959] были произведены какие-либо замены. Если метка [117960]label[117961] не указана, ветка в конце скрипта.[12210]Давайте посмотрим, как это работает, я добавлю [117786]p[117787]rint в нужном месте:[12211]OUTPUT:[12212]Вы можете видеть, что между тремя разными [117788]t[117789]ests существует разное количество пустого места. Это результат порядка, в котором [117790]sed[117791] отрицает символы, что означает, что [117792]белые пробела[117793] также отрицаются, при условии, что их четное количество. [12213]Команда:[12214]s/[117795]выбирает первый [117796]\(.[117797]символ[117798]\)[117799] в строке, которую можно выбрать [117800]\1[117801]дважды, [117802]и[12215]\(..[117805]все[117806]*\)[117807] символы между ними. Затем [117808]/[117809]заменяет[117810]/[117811] всю выборку на [117812]только [117813] выборку [117814]\2[117815]между ними. [117816]:[117817]Rinse, repea[117818]t[117819]. [12216]OPTIMIZE IT![12217]Мы можем улучшить производительность этой функции с помощью [12218]очень большого поля[12219]с простым добавлением в команду только еще двух символов, только так:[12220]OUTPUT[12221]sed[117823] теперь выполняет отрицания на любой [117824]последовательности[117825] из 1 или более символов, которые могут быть дважды выбраны, поэтому [117826]идентичный[117827] не появляется в приведенной выше команде - он полностью отрицается на первом проходе. [12222]И без [117828]p[117829]rint результаты одинаковы:[12223]OUTPUT[12224]POSITIVE FROM NEGATIVE[12225]Для отрицания отрицания требуется очень мало - и больше никакой рекурсии. [12226]OUTPUT:[12227]Или даже:[12228]OUTPUT:[12229]
чтобы быть эффективным и избежать многого подобного sed, работаем с (временным ) индексным файлом с одним элементом на строку.
Сейчас это не самый лучший способ создания огромного файлового индекса, особенно в связи с экспоненциальным временем, запрашиваемым на длину последовательности и индекса. AWK, безусловно, быстрее при однократном тестировании инструментов на одну последовательность и только память (нет временного файла, который съедает время на управление)[116997].