3

What is a simple way to find matching consecutive pairs of parentheses and replace them with their enclosing content using sed/awk in bash?

A minimal example would be:

Input:

(body1)

Output:

body1

Insufficient solution:

This could be done with

echo "(body1)" | sed 's/[()]//g'

Extended problem

But simply removing all opening/closing parentheses will not suffice, since the ultimate goal is to remove certain, not all (tex) commands from a source file, such as

Input:

Alea {\color{red}iacta} est. \textbf{Hic} forum est, populus {\color{red}properant}.

Output:

Alea iacta est. \textbf{Hic} forum est, populus properant.

So far I only managed to extract the text with:

awk -v FS="({\\color{red}|})" '{print $2}' $file.tex

Bonus

with sed -E 's/\{\\color\{red}([^{}]*)\}/\1/g' it is possible to remove only the \color{red} command - however, start and end of the command need to be on the same line.

How to remove a command that spans multiple lines before the closing parenthesis }?

Bonus Solution

If someone is interested, the following commands seem to solve the bonus problem: sed -i -r 's#\{\\color\{red\}([^}]*)\}#\1#g' $file.tex sed -i -r ':a;N;$!ba;s#\{\\color\{red\}([^}]*)\}#\1#' $file.tex

The first command removes all pairs of {\color{red} and } in a single line. The second command removes all pairs that span multiple lines.

  • 4
    Have a look at detex: code.google.com/archive/p/opendetex – Kusalananda Feb 15 '17 at 9:27
  • 2
    From a computer science point of view what you ask for is simply not possible to do with sed, as sed processes only regular expressions, and parsing matching parentheses that are arbitrarily deeply nested is beyond regular expressions. (Regular expressions are type 3 grammars which can be parsed by finite automatons as implemented by sed, grep, etc., but you present a type 2 grammar which requires a pushdown automaton that has access to a stack.) ~ For a fixed number of parentheses you may use regular expressions, but those are not nice to formulate and hard to read. – countermode Feb 15 '17 at 9:51
  • Ok and what about non-nested oocurrences? – Oscillon Feb 15 '17 at 10:15
  • @Kusalananda, reading through the wiki of detex, I did not find any hint on selectively removing certain commands, while others remain untouched. – Oscillon Feb 15 '17 at 11:19
  • 2
    But if you tried running detex, you'd see that it does know which command arguments are output text and which are settings, at least for the core commands. It removes all commands but keeps the appropriate text (e.g. the arguments to \emph{}). If you explicitly need to keep certain commands as-is (\textbf in your example) you should clarify your question. – Stephen Kitt Feb 15 '17 at 16:11
0

Even the simple question you're starting with hides some complexity. I'd start with

sed -E 's/\(([^()]*)\)/\1/'

repeated until there are no parenthesis pairs. This replaces the innermost text:

$ echo "((body))" | sed -E 's/\(([^()]*)\)/\1/'
(body)

As suggested by Kusalananda though, to strip TeX commands you should check out detex which is available in TeX Live (and in most distributions). Such processing requires more than matching parentheses or braces: you need to know a little about various commands' behaviour. Even in your example, \color needs to be processed one way, \textbf another...

  • Based on your input, I ended up using: sed -E 's/\{\\color\{red}([^{}]*)\}/\1/g'. The downside is, that it does not extend over multiple rows. – Oscillon Feb 15 '17 at 13:58

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.