Issue
I am currently writing a bash script to modify the output of my LaTeX compilations to have only what I find relevant printing on the console. As I would like this script to be extremely thorough, I set up different options to toggle different output filters at the same time depending of the nature of the informations given through the compilation (Fatal error, warning, over/underfull h/vbox...).
For those who may not know, we often need to perform several compilations in a row to have a full LaTeX document with correct labels, page numbering, index, table of contents... + other commands like bibtex
or makeglossaries
for bibliography and, well, glossaries. I therefore have a loop that execute everything and stops if there is a fatal error encountered, but should continue if it is only a minor warning.
My main command line is piping the pdflatex
output through a reversed grep
that finds errors line (starting by !
). Like this, the script stops only if grep
found a fatal error.
: | pdflatex --halt-on-error $@ | { ! grep --color=auto '^!.*' -A200; }
But when I activate any other filters (eg. '*.full.*'
for over/underfull lines), I need to be able to continue compiling to be able to identify it there is a major necessity to correct it (hey, sometimes, underfull lines are just not that ugly...).
That means my grep
command cannot be inverted as in the first line, and I cannot (or don't know how to) use the same grep
with a different regex. notice that if if using a different grep
, it should also be read from the pdflatex
output and I cannot pipe it directly following the above snippet.
To sum up, it should roughly look like this :
pdflatex --> grep for fatal errors --> if more filters, grep for those filters
--> pass to next step
I came up with several attempts that did not work properly :
This one works only if I want to compile WITH the warnings. Looking only for errors does not work.
latex_compilation() {
: | pdflatex --halt-on-error $@ | tee >({ ! grep --color=auto '^!.*' -A200; }) >({ grep --color=auto "$warnings_filter" -A5 };) >/dev/null
}
latex_compilation() {
: | pdflatex --halt-on-error $@ | tee >({ ! grep --color=auto '^!.*' -A200; }) >/dev/null | ({ grep --color=auto "$warnings_filter" -A5 };)
}
or even desperately
latex_compilation() {
: | pdflatex --halt-on-error $@ |
if [[ "$warnings_on" = true ]]; then
{ grep --color=auto "$warnings_filter" -A5 };
fi
{ ! grep --color=auto '^!.*' -A200; }
}
This one would work but uses 2 compilation processes for each step (you could easily go up to 7/8 compilations steps for a big and complex document). It should be avoided if possible.
latex_compilation() {
if [[ "$warnings_on" = true ]]; then
: | pdflatex --halt-on-error $@ | \
{ grep --color=auto "$warnings_filter" -A5 };
fi
: | pdflatex --halt-on-error $@ | \
{ ! grep --color=auto '^!.*' -A200; }
}
I spent hours looking for solutions online, but didn't find any yet. I really hope this is clear enough because it is a mess to sum up, moreover writing it. You can find the relavant code here if needed for clarity.
Solution
This one would work but uses 2 compilation processes
So let's use one.
latex_compilation() {
local tmp
tmp=$(pdflatext ... <&-)
if [[ "$warnings_on" = true ]]; then
grep --color=auto "$warnings_filter" -A5 <<<"$tmp"
fi
! grep --color=auto '^!.*' -A200 <<<"$tmp"
}
Or you can do that asynchronously, by parsing the output, in your chosem programmign langauge. For Bash see https://mywiki.wooledge.org/BashFAQ/001 :
line_is_warning() { .... }
latex_compilation() {
local outputlines=0 failed
while IFS= read -r line; do
if "$warnings_on" && line_is_warning "$line"; do
outputlines=5 # will output 5 lines after
fi
if [[ "$line" =~ ^! ]]; then
failed=1
outputlines=200 # will output 200 lines after
fi
if ((outputlines != 0)); then
((outputlines--))
printf "%s\n" "$line"
fi
done < <(pdflatext ... <&-)
if ((failed)); then return 1; fi
}
But Bash will be extremely slow. Consider using AWK or Python or Perl.
looking for solutions online
Exactly, you have to write a solution yourself, for your specific requirements.
his one works only if I want to compile WITH the warnings. Looking only for errors does not work.
You can write whole code blocks inside >( ... )
and basically anywhere. The exit status of a pipeline is the exit status of rightmost command (except set -o pipefail
). Put the failing command as the rightmost of the pipeline.
latex_compilation() {
pdflatex --halt-on-error "$@" <&- |
tee >(
if "$warnings_on"; then
grep --color=auto "$warnings_filter" -A5
else
cat >/dev/null
fi
) |
! grep --color=auto '^!.*' -A200
}
Answered By - KamilCuk Answer Checked By - Katrina (WPSolving Volunteer)