[Tex/LaTex] How to replace all $ $ by \( \)

editorsscripts

I want to replace all TeXish math $ $ in a tex file by LaTeXish \( \) and also $$ $$ by \[ \]. How can I do this cleverly?

Please beware that in TikZ environment, one can not replace $ $ by \( \).

Best Answer

Here's a perl script to get you started, subDollars.pl. As a disclaimer, you should test it significantly before using it on anything important. I can't guarantee the results, but it should get you started.

You can use it in the following way

  • perl subDollars.pl test.tex will simply output to the terminal with the substitutions
  • perl subDollars.pl -s test.tex will not output to the terminal
  • perl subDollars.pl -w test.tex will overwrite test.tex with the substitutions in place
  • perl subDollars.pl -o test.tex output.tex will output to output.tex with the substitutions in place.

Any environments that you don't want it to operate on should be included in

my %nosubstitutions = ("tikzpicture"=>1, "verbatim"=>1, "nosubblock"=>1);

This hash can consist of stand environments such as tikzpicture

\begin{tikzpicture}
\draw[red] ($(#2)+(-.5em,.9em)$) rectangle ($(RightPoint)+(0.2em,-0.3em)$);
\end{tikzpicture}

or any blocks of code that are not necessarily within an environment, but you can wrap them in a 'commented' environment, such as

%\begin{nosubblock}
\draw[red] ($(#2)+(-.5em,.9em)$) rectangle ($(RightPoint)+(0.2em,-0.3em)$);
%\end{nosubblock}

The script won't account for $...$ and $$...$$ split across lines- that's certainly doable, but a little more work.

subDollars.pl

#!/usr/bin/perl

use strict;
use warnings;           
use File::Copy;         # to copy the original file to backup (if overwrite option set)
use Getopt::Std;        # to get the switches/options/flags

# get the options
my %options=();
getopts("wos", \%options);

# standard output
my $out = *STDOUT;

# overwrite option
my $overwrite = 0;
$overwrite = $options{w};

# output file option
my $outputToFile = $options{o};

# can't call the script with MORE THAN 2 files
if(scalar(@ARGV)>2)
{
print $out <<ENDQUOTE

ERROR:
\t You're calling subDollars.pl with more than two file names
\t The script can take at MOST two file names, but you 
\t need to call it with the -o switch; for example

\t subDollars.pl -o originalfile.tex outputfile.tex

Exiting...
ENDQUOTE
;
    exit(2);
}

# check for output file
if($outputToFile and scalar(@ARGV)==1)
{
print $out <<ENDQUOTE
ERROR: When using the -o flag you need to call this script with 2 arguments

subDollars.pl -o "$ARGV[0]" [needs another name here]

Exiting...
ENDQUOTE
;
    exit(2);
}

# don't call the script with 2 files unless the -o flag is active
if(!$outputToFile and scalar(@ARGV)==2)
{
print $out <<ENDQUOTE

ERROR:
\t You're calling subDollars.pl with two file names, but not the -o flag.
\t Did you mean to use the -o flag ?

Exiting...
ENDQUOTE
;
    exit(2);
}

# array to store the modified lines
my @lines;

# hash naming environments that contain lines 
# that should not be substituted
my %nosubstitutions = ("tikzpicture"=>1, "verbatim"=>1, "nosubblock"=>1);

# switch to toggle nosubstitutions- initially off
my $nosubs = 0;

# if we want to over write the current file
# create a backup first
if ($overwrite)
{
    # original name of file
    my $filename = $ARGV[0];
    # copy it
    my $backupFile = $filename;
    my $backupExtension='.bak';

    $backupFile =~ s/\.tex/$backupExtension/;

    copy($filename,$backupFile) or die "Could not write to backup file $backupFile. Please check permissions. Exiting.\n";
}

# open the file
open(MAINFILE, $ARGV[0]) or die "Could not open input file";

# loop through the lines in the INPUT file
while(<MAINFILE>)
{
    # check for BEGIN of an environment that doesn't want substitutions
    $nosubs = 1 if( $_ =~ m/^\s*\\begin{(.*?)}/ and $nosubstitutions{$1} );

    # check for %\begin{nosubblock}
    $nosubs = 1 if( $_ =~ m/^\s*%\s*\\begin{(.*?)}/ and $nosubstitutions{$1} );

    # check for END of an environment that doesn't want substitutions
    $nosubs = 0 if( $_ =~ m/^\s*\\end{(.*?)}/ and $nosubstitutions{$1});

    # check for %\end{nosubblock}
    $nosubs = 0 if( $_ =~ m/^\s*%\s*\\end{(.*?)}/ and $nosubstitutions{$1} );

    # substitute $.*$ with \(.*\) 
    # note: this does NOT match $$.*$$
    s/(?<!\$)\$([^\$].*?)\$/\\\($1\\\)/g unless($nosubs);

    # substitute $$.*$$ with \[.*\]
    s/\$\$(.*?)\$\$/\\\[$1\\\]/g unless($nosubs);

    push(@lines,$_);

}

# output the formatted lines to the terminal
print @lines if(!$options{s});

# if -w is active then output to $ARGV[0]
if($overwrite)
{
    open(OUTPUTFILE,">",$ARGV[0]);
    print OUTPUTFILE @lines;
    close(OUTPUTFILE);
}

# if -o is active then output to $ARGV[1]
if($outputToFile)
{
    open(OUTPUTFILE,">",$ARGV[1]);
    print OUTPUTFILE @lines;
    close(OUTPUTFILE);
}

exit;

Here's a test file to test it on

before.tex

\documentclass{article}

\begin{document}

$x=a+b$

$x=a+b$ and another $x=a+b$, $x=a+b$ and another $x=a+b$

$$x=a+b$$ and another $$x=a+b$$, $$x=a+b$$ and another $$x=a+b$$

\begin{tikzpicture}
\draw[red] ($(#2)+(-.5em,.9em)$) rectangle ($(RightPoint)+(0.2em,-0.3em)$);
\end{tikzpicture}

\begin{verbatim}
\draw[red] ($(#2)+(-.5em,.9em)$) rectangle ($(RightPoint)+(0.2em,-0.3em)$);
\end{verbatim}

$$x=a+b$$ and another $$x=a+b$$, $$x=a+b$$ and another $$x=a+b$$

%\begin{nosubblock}
\draw[red] ($(#2)+(-.5em,.9em)$) rectangle ($(RightPoint)+(0.2em,-0.3em)$);
%\end{nosubblock}

$x=a+b$ and another $x=a+b$, $x=a+b$ and another $x=a+b$

$$x=a+b$$ and another $$x=a+b$$, $$x=a+b$$ and another $$x=a+b$$
\end{document}

after.tex

\documentclass{article}

\begin{document}

\(x=a+b\)

\(x=a+b\) and another \(x=a+b\), \(x=a+b\) and another \(x=a+b\)

\[x=a+b\] and another \[x=a+b\], \[x=a+b\] and another \[x=a+b\]

\begin{tikzpicture}
\draw[red] ($(#2)+(-.5em,.9em)$) rectangle ($(RightPoint)+(0.2em,-0.3em)$);
\end{tikzpicture}

\begin{verbatim}
\draw[red] ($(#2)+(-.5em,.9em)$) rectangle ($(RightPoint)+(0.2em,-0.3em)$);
\end{verbatim}

\[x=a+b\] and another \[x=a+b\], \[x=a+b\] and another \[x=a+b\]

%\begin{nosubblock}
\draw[red] ($(#2)+(-.5em,.9em)$) rectangle ($(RightPoint)+(0.2em,-0.3em)$);
%\end{nosubblock}

\(x=a+b\) and another \(x=a+b\), \(x=a+b\) and another \(x=a+b\)

\[x=a+b\] and another \[x=a+b\], \[x=a+b\] and another \[x=a+b\]
\end{document}