3

How to remove a specific latex command from the text AND close bracket behind it, but keep the text inside the brackets? The command in the following example to remove is \edit{<some staff>}. \edit{ and } should be removed, whereas <some staff> should be left unchanged.

Please fill free to suggest SED, AWK, Perl or whatever will do the job

senseless example:

We \edit{Introduce a} model for analyzing \emph{data} from various
experimental designs, \edit{such as paired or \url{http://www/}
longitudinal; as was done 1984 by NN \cite{mycitation} and by NNN
\cite{mycitation2}}.

Note that there might be one or more latex commands in the form \command{smth} inside \edit{} statements. \command{smth} should be left as it was

Output:

We Introduce a model for analyzing \emph{data} from various
experimental designs, such as paired or \url{http://www/}
longitudinal; as was done 1984 by NN \cite{mycitation} and by NNN
\cite{mycitation2}.

PS. I am introducing a lot of small edits to my text file. I want those edits to be highlighted, so my collaborator can see them. But afterward, I would like to remove all highlights and send the text to a reviewer.

The question was originally asked at AWK/SED Remove a specific latex command from the text AND closing bracket behind it. But for example there was too soft

3
  • you might want to explicitly state that you want to handle nested commands too. And if you want to handle nesting to arbitrary depth, or if it's enough to handle just one level. (Plain regexes can't handle counting parenthesis... you need more logic or witchcraft with Perl REs to do that.)
    – ilkkachu
    Jun 27, 2017 at 22:04
  • @likkachu Great point. Thank you. Updated the question
    – den2042
    Jun 27, 2017 at 22:14
  • The path, it seems, is to find \edit{ then look for the first }, which is not preceded by the { . But I definitively have no idea how to implement this
    – den2042
    Jun 27, 2017 at 22:21

3 Answers 3

3

Here's one that works in the simple case of only one level of commands within an \edit{...}, at maximum:

perl -00 -lpe 's,\\edit\{( (?: [^}\\]* | \\[a-z]+\{[^}]*\} )+ )\},$1,xg'

The middle part (?: [^}\\]* | \\[a-z]+\{[^}]*\} )+ has to alternatives: [^}\\]* matches any string with no closing brace or backslash (regular text); and \\[a-z]+\{[^}]*\} matches anything with backslash, lowercase letters, and then a matched pair of braces (like \url{whatever...}). The grouping (?:...)+ repeats those alternatives, and the outer parenthesis capture, so we can replace the match with just the part inside \edit{...}.

-00 tells Perl to handle the input one paragraph at time, with empty lines separating paragraphs. If you need to handle tags that span paragraphs, change that to -0777 to handle the whole input in one go (-0 for NUL-separated input would also work for that as a text file won't have any).

For your example, this seems to work, giving:

We Introduce a model for analyzing \emph{data} from various
experimental designs, such as paired or \url{http://www/}
longitudinal; as was done 1984 by NN \cite{mycitation} and by NNN
\cite{mycitation2}.

However, it (predictably) fails for an input with two levels of commands inside the \edit{...}:

Some \edit{\somecmd{\emph{nested} commands} here}.

Turns to:

Some \somecmd{\emph{nested} commands here}.

(the wrong closing brace is removed)


Actually handling balanced parenthesis is somewhat more tricky, it's discussed e.g. in this question on SO: Perl regular expression: match nested brackets.

2
  • This is an excellent answer. But later on I've discovered that my example was too soft. This one-liner is a trivial expansion of the answer and it covers formulas, percents and section commands: perl -00 -lpe 's,\\edit\{( (?: [^}\$\\]* | \$[^\$]*\$ | \\% | \\[a-zA-Z\[\] ]+\{[^}]*\} )+ )\},$1,xg' The solution in the main answer might mess up latex formulas because they do not start with \ but they may contain }
    – den2042
    Jun 29, 2017 at 12:44
  • -00 is for paragraph mode, -0 for NUL delimited; use -0777 for slurp mode. Feb 7, 2023 at 6:38
0

I have a solution based on Python, not concise enough but performs well with nested commands.

def command_remove(tex_in, keywords):
    # Romove command with curly bracket
    # keywords: "hl textbf" mean removing \hl{} and \textbf{}
    pattern = '\\\\(' + keywords.replace(' ', '|') + '){'
    commands = re.finditer(pattern, tex_in)
    idxs_to_del = [] # The index of }
    for command in commands:
        stack = 0
        current_loc = command.span()[1]
        while not (tex_in[current_loc] == '}' and stack == 0):
            if tex_in[current_loc] == '}':
                stack = stack - 1
            if tex_in[current_loc] == '{':
                stack = stack + 1
            current_loc = current_loc + 1
        idxs_to_del.append(current_loc)

    idxs_to_del = sorted(idxs_to_del, reverse=True) # sort
    tex_list = list(tex_in)
    for idx in idxs_to_del:
        tex_list.pop(idx) # remove }

    tex_out = ''.join(tex_list)
    tex_out = re.sub(pattern, '', tex_out) # remove \xxx{
    return tex_out

It locates the target command by regular expression and then locates the position of the closing bracket with a stack. For tex_out = command_remove(tex_in, "revise textbf") with tex_in:

\hl{Can you} \revise{can a \textbf{can} as a \emph{canner} can} can a can?

we would get tex_out:

\hl{Can you} can a can as a \emph{canner} can can a can?

More details, i.e., command line running, are in Latex_command_remove.

0

To handle \edit{...}s with LaTeX commands (meaning other {...} pairs) within, you can use perl's ability to handle recursion in its regexps:

perl -pe 's{\\edit(\{((?:[^{}]++|(?1))*)\})}{$2}g' file

Where (?1) recalls the regexp inside the first pair of (...), here the one matching a {...} pair.

(here not handling escaped braces or \verb or comments and assuming \edit{...}s don't span several lines all of which could be added fairly easily if needed).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .