Я думаю, вам может потребоваться указать свое неквалифицированное доменное имя (имя хоста) в / etc / hostname
и ваше полное доменное имя (hostname.com) в качестве первой записи после 127.0.0.1 в / etc / hosts
. Я не уверен, что такое IP-адрес 1.1.1.1, но я думаю, что эту строку следует удалить. Этот вопрос askbuntu может помочь прояснить ситуацию, хотя я до сих пор не уверен, как вы получили эти результаты.
Я уверен, что получу удар по голове за использование петли, но все же ... вот один способ сделать это.
#!/bin/bash
while read -r line; do
sed -n "/$line/,/^C/p" fileA | sed '$d'
done < fileB
Пример:
./bacteria.sh
C 02030 *Bacterial chemotaxis* [PATH:aap02030]
D NT05HA_0919 maltose-binding periplasmic protein
D NT05HA_0918 maltose-binding periplasmic protein
C 03070 *Bacterial secretion system* [PATH:aap03070]
D NT05HA_1309 protein-export membrane protein SecD
D NT05HA_1310 protein-export membrane protein SecF
D NT05HA_1819 preprotein translocase subunit SecE
D NT05HA_1287 protein-export membrane protein
Где fileA
и fileB
- ваши файлы примеров.
Структура регулярных выражений:
sed -n "/$line/,/^C/p" fileA | sed '$d'
Вывести строки между $ line
и следующей строкой, начинающейся с буквы C
, но исключить ( sed '$ d'
) последняя строка, поскольку она используется просто как «маркер остановки».
sed --version
sed (GNU sed) 4.2.2
bash --version
GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)
Общее решение, которое охватывает все размеры ваших файлов: https://www.gnu.org/software/parallel/man.html#EXAMPLE:-Grepping- n-строк-для-m-регулярных-выражений
ПРИМЕР: выделение n строк для m регулярных выражений.
Самым простым решением для большого количества регулярных выражений с помощью grep является:
grep -f regexps.txt bigfile
Или, если регулярные выражения представляют собой фиксированные строки:
grep -F -f regexps.txt bigfile
Есть 3 ограничивающих фактора: ЦП, ОЗУ и дисковый ввод-вывод.
ОЗУ легко измерить: если процесс grep занимает большую часть вашей свободной памяти (например, при запуске в верхней части), то ОЗУ является ограничивающим фактором.
CPU также легко измерить: если grep занимает> 90% CPU в верхней части, то CPU является ограничивающим фактором, и распараллеливание ускорит это.
Труднее увидеть, является ли дисковый ввод-вывод ограничивающим фактором, и в зависимости от дисковой системы распараллеливание может быть быстрее или медленнее. Единственный способ узнать наверняка - это проверить и измерить.
Ограничивающий фактор: RAM
Обычный bigfile grep -f regexs.txt работает независимо от размера bigfile, но если regexps.txt настолько велик, что не помещается в памяти, то вам необходимо разделить его.
grep -F занимает около 100 байт ОЗУ, а grep занимает около 500 байт ОЗУ на 1 байт регулярного выражения. Поэтому, если regexps.txt занимает 1% вашей оперативной памяти, он может быть слишком большим.
Если вы можете преобразовать регулярные выражения в фиксированные строки, сделайте это. Например. если все строки, которые вы ищете в большом файле, выглядят так:
ID1 foo bar baz Identifier1 quux
fubar ID2 foo bar baz Identifier2
, тогда ваш regexps.txt может быть преобразован из:
ID1.*Identifier1
ID2.*Identifier2
в:
ID1 foo bar baz Identifier1
ID2 foo bar baz Identifier2
Таким образом вы можете использовать grep -F, который занимает примерно на 80% меньше памяти и намного быстрее.
Если он по-прежнему не помещается в памяти, вы можете сделать следующее:
parallel --pipepart -a regexps.txt --block 1M grep -F -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
1M должен быть вашей свободной памятью, разделенной на количество ядер и разделенной на 200 для grep -F и на 1000 для обычного grep. В GNU / Linux вы можете:
free=$(awk '/^((Swap)?Cached|MemFree|Buffers):/ { sum += $2 }
END { print sum }' /proc/meminfo)
percpu=$((free / 200 / $(parallel --number-of-cores)))k
parallel --pipepart -a regexps.txt --block $percpu --compress grep -F -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
Если вы можете жить с дублированными строками и неправильным порядком, это быстрее сделать:
parallel --pipepart -a regexps.txt --block $percpu --compress grep -F -f - bigfile
Ограничивающий фактор: CPU
Если CPU является ограничивающим фактором, распараллеливание должно выполняться на regexps:
cat regexp.txt | parallel --pipe -L1000 --round-robin --compress grep -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
Команда запускает одну команду grep для каждого процессора и читает большой файл один раз для каждого процессора, но поскольку это выполняется параллельно, все операции чтения, кроме первого, будут кэшироваться в ОЗУ. В зависимости от размера regexp.txt может быть быстрее использовать --block 10m вместо -L1000.
Некоторые системы хранения работают лучше при параллельном чтении нескольких фрагментов.Это верно для некоторых систем RAID и некоторых сетевых файловых систем. Чтобы распараллелить чтение большого файла:
parallel --pipepart --block 100M -a bigfile -k --compress grep -f regexp.txt
Это разделит большой файл на блоки по 100 МБ и запустит команду grep для каждого из этих блоков. Чтобы распараллелить чтение bigfile и regexp.txt, объедините их с помощью --fifo:
parallel --pipepart --block 100M -a bigfile --fifo cat regexp.txt \
\| parallel --pipe -L1000 --round-robin grep -f - {}
Если строка соответствует нескольким регулярным выражениям, строка может быть продублирована.
Большая проблема
Если проблема слишком велика, чтобы ее решить, вы, вероятно, готовы к Lucene.
Если вы хотите точно сопоставить часть, которая находится между C
и [ПУТЬ :. ..]
(и если предположить, что те *
в вашем примере предназначены только для акцента, а не являются частью фактических данных), вы можете сделать:
awk '
!start {all_strings[$0]; next}
/^C/ {
key = $0
# strip the leading C <word>:
sub(/^C[[:blank:]]+[^[:blank:]]+[[:blank:]]*/, "", key)
# strip the trailing [...]:
sub(/[[:blank:]]*\[[^]]*][[:blank:]]*$/, "", key)
selected = key in all_strings
}
selected' fileB start=1 fileA
Помимо дополнительной надежности (например, Бактериальная секреция
будет соответствовать только записи Бактериальной секреции
, но не также Бактериальной секреторной системе
), она также очень эффективна в том смысле, что файлы считываются только один раз и соответствие - это только один поиск по хеш-таблице, в отличие от нескольких поисков подстрок или сопоставлений регулярных выражений.
Данные в fileA
разделены на записи, которые начинаются с C
в новой строке. Каждая запись разделена на целые поля, которые начинаются с D
в новой строке.
Нам нужно прочитать строки из fileB
и использовать их для запроса первого поля каждой записи в fileA
:
while read -r query; do
awk -vq="$query" 'BEGIN { RS="^C|\nC"; FS=OFS="\nD" } $1 ~ q {print "C" $0}' fileA
done <fileB
Я устанавливаю разделитель записей ( RS
) для сопоставления либо C
в начале строки , либо после новой строки, либо мы не сможем правильно сопоставить что-либо в первой записи. Я использую переменную awk
, q
, чтобы удерживать значение, прочитанное из файла, и сравниваю первое поле каждой записи с этим значением.
Результат:
C 02030 *Bacterial chemotaxis* [PATH:aap02030]
D NT05HA_0919 maltose-binding periplasmic protein
D NT05HA_0918 maltose-binding periplasmic protein
C 03070 *Bacterial secretion system* [PATH:aap03070]
D NT05HA_1309 protein-export membrane protein SecD
D NT05HA_1310 protein-export membrane protein SecF
D NT05HA_1819 preprotein translocase subunit SecE
D NT05HA_1287 protein-export membrane protein
Вы можете использовать awk
:
awk 'NR==FNR{ # On the first file,
a[$0]; # store the content in the array a
next
}
{ # On the second file,
for(i in a) # for all element in the array a,
if(index($0,i)) { # check if there is match in the current record
print "C" $0 # in that case print it with the record separator
next
}
}' fileB RS='\nC' fileA
C 02030 *Bacterial chemotaxis* [PATH:aap02030]
D NT05HA_0919 maltose-binding periplasmic protein
D NT05HA_0918 maltose-binding periplasmic protein
C 03070 *Bacterial secretion system* [PATH:aap03070]
D NT05HA_1309 protein-export membrane protein SecD
D NT05HA_1310 protein-export membrane protein SecF
D NT05HA_1819 preprotein translocase subunit SecE
D NT05HA_1287 protein-export membrane protein
с grep
и sed
в одной строке:
for i in $(grep -f fileb filea | awk '{print $2}'); do sed -e 's/^C/\nC/g' filea | sed -n "/$i/,/^ *$/p" | grep -v "^$"; done
То есть:
Возьмите шаблоны для поиска с помощью:
grep -f fileb filea | awk '{print $2}'
Вставьте пустую строку перед каждой строкой, начинающейся с C:
sed -e 's/^C/\nC/g' filea
Взять из шаблона в пустая строка:
sed -n "/$i/,/^ *$/p"
Удалите пустые строки, чтобы получить желаемый результат:
grep -v "^$"
Все это внутри цикла for, чтобы иметь возможность выполнять один и тот же процесс для каждого шаблона в файле.
Bash использует только встроенные команды для генерации вывода. Логика кода довольно проста и не требует пояснений.
while IFS= read -r line; do
IFS=$'\n'; for pat in $(< fileB); do
case $line in
[C]*"$pat"* )
echo "$line"
unset flag
break
;;
[D]* )
${flag+":"} echo "$line"
${flag+":"} break
;;
* ) flag=;;
esac
done
done < fileA
С GNU sed мы загружаем шаблоны в область хранения, а затем в данных файла B, мы сравниваем их, чтобы определить, печатать ли блоки C-D + или нет, в зависимости от того, было ли найдено совпадение в строке C с использованием шаблоны, хранящиеся на удержании. Файл B не должен начинаться с C или D.
sed -e '
/^D/bD
/^C/{
x;G
/\n\(.*\)\n\(.*\n\)\{0,1\}[^\n]*\1[^\n]*$/{
s/\(.*\)\n.*/\1/;x
:loop
n
/^C/!bloop
s/^/\n/;D
}
s/\(.*\)\n.*/\1/;x
:D
$!N;D
}
H;d
' fileB fileA
perl -l -0777ne '
push(@A, split $\), next if @ARGV;
for my $pat ( map { quotemeta } @A ) {
while ( /^C [^\n]* $pat [^\n]*$/xmg ) {
my $C = $&;
print $C .= $& if /\G(\nD.*$)+/xm;
}
}
' fileB fileA