Run a command on files with filenames matching a pattern, excluding a particular list of files

-L flag to Grep list files not matching a pattern. You want -l instead. Also, Grep needs to see double-backslash to match a single backslash.

Since you are in Bash, let us get hold of some useful constructs.

#!/bin/bash -
shopt -s globstar extglob
mapfile -t -d "" filenames < <(grep -Zl '\\RequireLuaTeX' ./**/!(foo|bar|baz).tex)
rm -f "${filenames[@]/%.tex/.pdf}"
latexmk -pdf -shell-escape -interaction=nonstopmode "${filenames[@]}"
  • **/!(foo|bar|baz).tex expands to all files in the current directory tree that end in .tex but whose basename is not foo.tex, bar.tex nor baz.tex. Both globstar and extglob are required for this operation.

  • "${filenames[@]/%.tex/.pdf}" expands to all elements of the array, substituting each trailing .tex by .pdf.

Since Latexmk can be given multiple files as arguments, we could skip for-loops.


With zsh, you can turn an array into a pattern that matches any of its elements by joining with | with the j[|] parameter expansion flag the elements inside which the glob characters have been escaped with the b parameter expansion flag:

#! /bin/zsh -
set -o extendedglob
excluded_file_names=(foo.tex bar.tex baz.tex)
excluded_file_names_pattern="(${(j[|])${(@b)excluded_file_names}})"

# here using the ~ extendedglob operator to apply the exclusion
tex_files=(
  ./**/(*.tex~$~excluded_file_names_pattern)
)

files=(
  ${(0)"$(grep -lZF '\RequireLuaTeX' $tex_files)"}
)
rm -f ${files/%tex/pdf}
latexmk -pdf -shell-escape -interaction=nonstopmode $files

Or you could use the e glob qualifier to check if the tail of the file path is in the array:

#! /bin/zsh -
excluded_file_names=(foo.tex bar.tex baz.tex)

tex_files=(
  ./**/*.tex(^e['(($excluded_file_names[(Ie)$REPLY:t]))'])
)

files=(
  ${(0)"$(grep -lZF '\RequireLuaTeX' $tex_files)"}
)
rm -f ${files/%tex/pdf}
latexmk -pdf -shell-escape -interaction=nonstopmode $files

The way I approach this kind of problem is to turn the list of file names/patterns into a hash that has instant lookup with no searching required. (Note that the excludedFiles patterns such as z*.tex are expanded as part of the assignment, not as part of the hashing loop. For example, if there are three files matching the z*.tex glob, then excludedFiles will contain three entries rather than the one pattern, and the hashing loop will iterate three times.)

# User configurable list of files and patterns
excludedFiles=(foo.tex bar.tex baz.tex z*.tex)

# Convert the list into a hash
declare -A excludedHash
for excludedFile in "${excludedFiles[@]}"
do
    [[ -e "$excludedFile" ]] && excludedHash[$excludedFile]=yes
done

# Processing
for filename in "${filenames[@]}"
do
    [[ -n "${excludedHash[$filename]}" ]] && continue    # Skip if filename is in hash

    base="${filename%.*}"
    rm -f "$base".pdf
    latexmk -pdf -shell-escape -interaction=nonstopmode  "$base".tex
done