Skrypt do wyodrębnienia wybranych pozycji z pliku bibtex

11

Mam duży plik bibtex z wieloma wpisami, z których każdy ma ogólną strukturę

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

(w niektórych przypadkach ARTICLEmoże to być inne słowo np. BOOK)

Chciałbym napisać prosty skrypt (najlepiej skrypt powłoki), aby wyodrębnić wpisy o podanym AuthorYear i umieścić je w nowym pliku .bib.

Mogę sobie wyobrazić, że potrafię rozpoznać pierwsze zdanie wpisu AutorYeara i ostatnie po pojedynczym zamknięciu }i być może użyć go seddo wyodrębnienia wpisu, ale tak naprawdę nie wiem, jak to zrobić dokładnie. Czy ktoś może mi powiedzieć, jak to osiągnę?

Prawdopodobnie powinno to być coś w rodzaju

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

Ale to zatrzymuje się z powodu zamknięcia }w pierwszym elemencie wpisu, co daje ten wynik:

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

Muszę więc rozpoznać, czy }jest to jedyny znak w wierszu i czy „sed” przestaje czytać, gdy tak jest.

Michiel
źródło
Mogłem tylko zmodyfikować kod trochę: sed -n "/AuthorYear/,/\}$/p". Zwróć uwagę na $symbol. Działa dobrze, z tym wyjątkiem, że nie drukuje zamknięcia }bibitemu. Btw, czy użycie jest sedkonieczne?
Barun,
@Barun użycie sedwcale nie jest konieczne, pomyślałem, że będzie to najłatwiejsza opcja. Wymyśliłem nieco inny kod: sed -n "/AuthorYear/, /^ *\}/p"który wydaje się robić dokładnie to, co chcę, w tym zamykanie }i korygowanie spacji, jeśli takie istnieją
Michiel

Odpowiedzi:

2

Poniższy skrypt w języku Python wykonuje wymagane filtrowanie.

#!/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,

Osobiście wolę przejście na język skryptowy, gdy logika filtrowania staje się złożona. Być może ma to przynajmniej zaletę w zakresie współczynnika czytelności.

Barun
źródło
Ostrożnie, istnieje wiele wpisów z zagnieżdżonymi literami {}s. Jeśli możesz upewnić się, że wejście się skończy \n}, możesz zatrzymać się na^}
vonbrand
8

Polecam użycie języka z przetestowaną w bitwie biblioteką BibTeX zamiast odkrywania tego koła. Na przykład

#!/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;
}

Prawdopodobnie będziesz musiał zainstalować moduł: cpan install BibTeX::Parser

Glenn Jackman
źródło
1

Teraz mamy również moduł bibparsing Pythona, który pozwala analizować bazy danych BibTeX za pomocą Pythona. Na przykład używam następującego skryptu, aby obliczyć liczbę autorów we wspólnych pracach:

#!/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])
wzab
źródło
1

Inną opcją byłoby użycie bibtoola.

Przykład:

bibtool -- select{$key AuthorYear”} input.bib -o output.bib

Zapoznaj się z instrukcją dla konkretnych przypadków.

Kirk Walla
źródło
0

Jest to skrypt Bash, który odczytuje każdą linię i używa dopasowania wyrażenia regularnego do wyodrębnienia każdego wpisu, który ma wymagany wzorzec w głowie. Możesz to nazwać getbibslub coś takiego:

#!/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"

Aby wyodrębnić wszystkie wpisy z autorem roku 1989, możesz:

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

Mogą występować pewne problemy, których jeszcze nie przetestowałem, ale wydaje się, że działają dobrze dla tego zadania.


źródło
0

Żeby być kompletnym, sposób, w jaki sam się zorientowałem, nie jest tak miły jak niektóre inne, ale działa:

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

Można go uruchomić z wiersza polecenia lub umieścić w skrypcie bash.

Michiel
źródło