4

Is it easy to do the following script? I have my LaTeX-documents on /home/jaakko/Documents/LaTeX and its subdirectories. I would like to have a script watch the .tex documents in that directory and its subdirectories and if some tex-file changes, the script tries to compile it as soon as possible (pdflatex file.tex). How can I do that in Bash?

6
  • I don't think you can "watch" a directory, but you could write a cron job...
    – eyoung100
    Commented Oct 20, 2014 at 18:50
  • 3
    On Linux you can use inotify to watch a directory.
    – Barmar
    Commented Oct 20, 2014 at 19:16
  • This looks just like what an IDE would do... Texmaker, Latexila, ... Commented Oct 20, 2014 at 20:09
  • Here's an answer that uses inotify and might solve your problem: Continuously compile/watch source files
    – Marco
    Commented Oct 20, 2014 at 20:52
  • My distribution (Xubuntu) has no command inotifywait but it has inotify. Still it says ./compile.sh: line 2: inotify: command not found. At least man inotify works but man inotifywait does not work.
    – Jaakko
    Commented Oct 20, 2014 at 21:11

2 Answers 2

4

It can also be done with fswatch:

fswatch -o filename.tex | xargs -n1 -I{} pdflatex filename.tex
2
  • This should be the accepted answer. Very nice
    – yiwen
    Commented Jan 31, 2021 at 20:03
  • fswatch is too much platform agnostic and doesn't appear to distinguish between read and write at least on Linux: most events are named "PlatformSpecific" which is an issue. pdflatex filename.tex triggers again fswatch by reading the file.
    – A.B
    Commented May 16, 2021 at 11:28
2

First of all, what you're trying to do is the usual job of an IDE. Whether it's Texmaker, Latexila, or another one, IDEs wil allow you recompile LaTeX code extremely fast, and some may permit automatic recompiling at given intervals.

Many IDEs nowadays rely on inotify (which is a C API) to detect file changes. However, the number of inotify watches is limited by the system configuration, and well... I took the challenge of writing an actual bash script doing the job.

Anyway, here's a little idea using find and MD5 hashes :

  • I'm using find to find all .tex files.
  • For each file, I call a function (update_file) which checks whether the file has changed since last time, and calls pdflatex if necessary.
  • File changes are detected through md5sum changes. Each file can be associated with a MD5 hash (obtained through md5sum file). If the file contents change, so does the hash. Therefore, I can monitor file changes by monitoring MD5 hashes changes.

Basically, I am using a md5.sum file to store all MD5 hashes associated with TeX files. When a file is modified, its hash changes, and therefore, is no longer the same as in the MD5 file. When this happens, the script calls pdflatex and updates the new MD5 hash.

Here is the code, I added some information in the comments. Feel free to adjust it, and change the variables set in the beginning. However, always use absolute paths.

#!/bin/bash

# 
# Defining a few variables...
#
LATEXCMD="/usr/bin/pdflatex"
LATEXDOC_DIR="/home/jaakko/Documents/LaTeX"
MD5SUMS_FILE="$LATEXDOC_DIR/md5.sum"

#
# This function checks whether a file needs to be updated,
# and calls LATEXCMD if necessary. It is called for each
# .tex file in LATEXDOC_DIR (see below the function).
#
update_file()
{
    [[ $# -ne 1 ]] && return;
    [[ ! -r "$1" ]] && return;

    # Old MD5 hash is in $MD5SUMS_FILE, let's get it.
    OLD_MD5=$(grep "$file" "$MD5SUMS_FILE" | awk '{print $1}')

    # New MD5 hash is obtained through md5sum.
    NEW_MD5=$(md5sum "$file" | awk '{print $1}')

    # If the two MD5 hashes are different, then the files changed.
    if [ "$OLD_MD5" != "$NEW_MD5" ]; then
        echo "$LATEXCMD" -output-directory $(dirname "$file") "$file"

        # Calling the compiler.
        "$LATEXCMD" -output-directory $(dirname "$file") "$file" > /dev/null
        LTX=$?

        # There was no "old MD5", the file is new. Add its hash to $MD5SUMS_FILE.
        if [ -z "$OLD_MD5" ]; then
            echo "$NEW_MD5 $file" >> "$MD5SUMS_FILE"
        # There was an "old MD5", let's use sed to replace it.
        elif [ $LTX -eq 0 ]; then
            sed "s|^.*\b$OLD_MD5\b.*$|$NEW_MD5 $file|" "$MD5SUMS_FILE" -i
        fi
    fi
}

# Create the MD5 hashes file.
[[ ! -f "$MD5SUMS_FILE" ]] && touch "$MD5SUMS_FILE"

IFS=$'\n'
find "$LATEXDOC_DIR" -iname "*.tex" | while read file; do
    # For each .tex file, call update_file.
    update_file "$file"
done

Now, if you want to run this script periodically, you can use watch :

$ watch /path/to/script.sh

You may use the -n switch to adjust the refresh time:

$ watch -n 2 /path/to/script.sh

You can place this script in /home/jaakko/Documents/LaTeX and run it whenever you're developing.

You must log in to answer this question.

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