3

I have a file test.tex with content that is similar to this:

\documentclass{scrartcl}
\usepackage{graphicx}
\title{Test}
\author{Author 1, Author 2, Author 3}
\begin{document}
\end{document}

I want to extract every author that is written in the { ... }. Therefore I did the following:

authors=$(cat test.tex | grep '\author' | tr -d '\author' | tr -d '{' | tr -d '}' )

This code works only for this case. My problem is that

  1. there could be [] instead of {}
  2. the line could span over multiple lines like the following example

\author{Author 1,

Author 2,

Author 3}

Does anyone know how to solve these two problems?

2
#!/bin/bash

sed -nr '
/\\author/ {
    :ending
    /]|}$/! {
        N   
        b ending 
    }
    s/\\author(\{|\[)(.*)(}|])/\2/p
}
' test.tex

Explanation (code the same, but comments added):

#!/bin/bash

sed -nr '
# if the line contains the \author string, we are working with it.
/\\author/ {

    ##### this part are needed for multiple line pattern processing

    # put a label here. We will be return to this point, 
    # until we reach line, which have } or ] in the ending.
    :ending

    # if this line does not ended by } or ]. 
    # It is tell us, that this line continues on the next line.
    /]|}$/! {

        # Take the next line and append it to the previous line. 
        # Just join them together.
        N   

        # Go to the ":ending" label
        b ending 
    }

    ##### ending multiple line pattern processing

    # remove the \author word and brackets from line
    s/\\author(\{|\[)(.*)(}|])/\2/p
}
' test.tex

test.tex

\documentclass{scrartcl}
\usepackage{graphicx}
\title{Test}
\author{Author 1, Author 2, Author 3}
\author[Author 1, Author 2, Author 3]
\author{Author 1,
Author 2,
Author 3}
\author[Author 1,
Author 2,
Author 3]
\begin{document}
\end{document}

output

Author 1, Author 2, Author 3
Author 1, Author 2, Author 3
Author 1,
Author 2,
Author 3
Author 1,
Author 2,
Author 3
  • Thanks! It worked and... I learned quite few from the explanation^^ – ADDB Jul 15 '17 at 20:04
3
grep -zPo '\\author{\K[^}]*' ex1.tex | tr '\0\n' '\n '

Some quick explanation notes:

  • -z input and output records ("lines") are separated by NULL (\0). So the full TeX file will be one single record.
  • -P Use Perl pcre regular expression variant.
  • -o output just the part of the record that matches the regExp.
  • \\author{\K means left context

The tr '\0\n' '\n ' changes the output record separator (\0 to \n) and removes NewLines inside names (\n to )

  • Would you mind explaining the command? – ADDB Jul 15 '17 at 16:06
2

This seems to do the job: egrep -o '[\[{]?Author' | sed -E 's/[\[{]//'

Examples:

1)

echo "\documentclass{scrartcl}
\usepackage{graphicx}
\title{Test}
\author[Author 1,
Author 2
Author 3 ] " | egrep -o '[\[{]?Author' | sed -E 's/[\[{]//'
Author
Author
Author

2)

echo "\documentclass{scrartcl}
\usepackage{graphicx}
\title{Test}
\author[Author 1, Author 2, Author 3]
\begin{document}
\end{document}" | egrep -o '[\[{]?Author' | sed -E 's/[\[{]//'
Author
Author
Author

3)

echo "\documentclass{scrartcl}
\usepackage{graphicx}
\title{Test}
\author{Author 1, Author 2, Author 3}
\begin{document}
\end{document}" | egrep -o '[\[{]?Author' | sed -E 's/[\[{]//'
Author
Author
Author

You can probably do it using only grep and lookbehinds and whatnot. I personally have no problems using a pipe into sed after grep.

  • This is close but not exactly. The author 1, author 2... Were supposed to be placeholder for actual names. (e.g. Author 1 = Ryan Banana) if you could change it so that that works too I'll accept your answer. – ADDB Jul 15 '17 at 16:05
2

Python

With your input file as given in the question, one liner can be done as so:

$ python -c 'import sys,re;f=open(sys.argv[1],"r");a=tuple(l for l in f.readlines() if l.startswith("\\author") );print("\n".join(re.split(", |,|{|}",a[0].strip())[1:]))' input.tex      
Author 1
Author 2
Author 3

And a script as so:

#!/usr/bin/env python

import sys,re

# read the doc, find the desired line
line=""
with open(sys.argv[1]) as f:
    for l in f:
        if l.startswith("\\author"):
            line=l.strip()
            break
# split at multiple separators, get slice of that list starting since 2nd item
author_list = re.split( ", |,|{|}", line )[1:] 
# print 1 author per line
print("\n".join(author_list))

The key steps are two fold - read the file and find the line that stars with \\authors string, and then break the line at multiple separators into list of tokens, and build a new-line separated string out of that list of tokens. I also took the liberty to consider possibility that you may have to split either at , or ,<space>.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.