Скрипт для извлечения выбранных записей из файла bibtex


11

У меня есть большой файл bibtex со многими записями, где каждая запись имеет общую структуру

@ARTICLE{AuthorYear,
item = {...},
item = {...},
item = {...},
etc
}

(в некоторых случаях это ARTICLEможет быть другое слово, например BOOK)

То, что я хотел бы сделать, это написать простой сценарий (желательно просто сценарий оболочки) для извлечения записей с заданным AuthorYear и поместить их в новый файл .bib.

Я могу себе представить, что я могу распознать первое предложение записи AuthorYear и последнее по одному закрытию }и, возможно, использовать sedдля извлечения записи, но я не знаю, как именно это сделать. Может кто-нибудь сказать мне, как я бы этого достиг?

Это должно быть что-то вроде

sed -n "/AuthorYear/,/\}/p" file.bib

Но это останавливается из-за закрытия }первого элемента записи, что дает следующий вывод:

@ARTICLE{AuthorYear,
item = {...},

Поэтому мне нужно узнать, }является ли символ в строке единственным, и «sed» перестает читать, только когда это так.


Я мог бы изменить только свой код немного: sed -n "/AuthorYear/,/\}$/p". Обратите внимание на $символ. Он работает нормально, за исключением того, что он не печатает закрытие }bibitem. Кстати, использование sedнеобходимо?
Barun

@ Барун использовать sedсовсем не обязательно, я просто подумал, что это будет самый простой вариант. Я понял немного другой код: sed -n "/AuthorYear/, /^ *\}/p"который, кажется, делает именно то, что я хочу, включая закрытие }и исправление пробелов, если они есть
Michiel

Ответы:


2

Следующий скрипт Python выполняет желаемую фильтрацию.

#!/usr/bin/python
import re

# Bibliography entries to retrieve
# Multiple pattern compilation from: http://stackoverflow.com/a/11693340/147021
pattern_strings = ['Author2010', 'Author2012',]
pattern_string = '|'.join(pattern_strings)
patterns = re.compile(pattern_string)


with open('bibliography.bib', 'r') as bib_file:
    keep_printing = False
    for line in bib_file:
        if patterns.findall(line):
            # Beginning of an entry
            keep_printing = True

        if line.strip() == '}':
            if keep_printing:
                print line
                # End of an entry -- should be the one which began earlier
                keep_printing = False

        if keep_printing:
            # The intermediate lines
            print line,

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


Осторожно, есть много записей с вложенными {}s. Если вы можете убедиться, что запись заканчивается \n}, вы можете остановиться на^}
vonbrand

8

Я бы рекомендовал использовать язык с проверенной в битве библиотекой BibTeX, а не изобретать это колесо. Например

#!/usr/bin/env perl
use strict;
use warnings;
use autodie;
use BibTeX::Parser;

open my $fh, '<', $ARGV[0];
my $parser = BibTeX::Parser->new($fh);
my @authoryear;
while (my $entry = $parser->next) {
    if ($entry->parse_ok) {
        if ($entry->key eq "AuthorYear") {
            push @authoryear, $entry;
        }
    }
    else {
        warn "Error parsing file: " . $entry->error;
    }
}

# I'm not familiar with bibtex files, so this may be insufficient
open my $out, '>', "authoryear.bib";
foreach my $entry (@authoryear) {
    say $out $entry->raw_bibtex;
}

Вам, вероятно, придется установить модуль: cpan install BibTeX::Parser


1

Теперь у нас также есть модуль разбора Python, который позволяет анализировать базы данных BibTeX с помощью Python. Например, я использую следующий скрипт для подсчета количества авторов в совместных документах:

#!/usr/bin/python
import sys
import bibtexparser as bp
with open(sys.argv[1]) as bibtex_file:
    bd = bp.load(bibtex_file)
    for art in bd.entries_dict:
    print("*********")
    ae = bd.entries_dict[art]
    print(ae[u'title'])
    auths=ae[u'author'].split(" and ")
    print(len(auths))
    print(auths[0]+" --- "+auths[-1])


0

Это скрипт Bash, который читает каждую строку и использует сопоставление с регулярным выражением для извлечения каждой записи, которая имеет требуемый шаблон в своей голове. Вы можете назвать это getbibsили что-то:

#!/usr/bin/env bash
# usage: ./getbibs pattern input.bib output.bib

while read entry; do
    if [[ $entry =~ ^@.*{$1,$ ]]; then
        printf "%s\n" "$entry" >> "$3"
        while read item; do
            [[ $item =~ ^@.*$ ]] && break
            printf "%s\n" "$item" >> "$3"
        done
    fi
done < "$2"

Чтобы извлечь все записи с автором 1989 года, вы можете сделать:

$ chmod +x ./getbibs
$ ./getbibs 1989 file.bib author.bib

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


0

Просто чтобы быть завершенным, способ, которым я понял сам, не так хорошо, как некоторые другие, но это работает:

entries=( AuthorYear1 AuthorYear2 )
for entry in "${entries[@]}" do
     sed -n "/"${entry}"/, /^ *\}/p" refs.bib 
done

Его можно запустить из командной строки или поместить в скрипт bash.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.