Few months later, I decided to learn a bit more about the command
find(1). The recursivity aspect of my build system could be solved with it through a "simple" shell. By using
the -exec construct, one could execute a single command on any occurence. Using a simple shell script with some arguments parsing, colored output, input validation was an appealing idea. And no dependencies on python3!
So I translated my python script into shell.
It is my first real use of the shell language (still quite arcane to me) with a lot of comments so that it also serves as a
personnal unix shell textbook.
It is almost as long as the python script and it smells like dirty coding.
By editing the hardcoded pathes and build variables, one can reuse this with another compiler like pandoc.
Anyway:
Code:
#!/bin/sh
#TODO why are the variable use as "${VAR}" or "{$VAR}" can't remember
#TODO make a sanitize function that check the Paths (like the python version). Also, if we are at least in the root folder or any subfolder (not anywhere else that would put ../../ in paths)
#Paths
ROOT="/home/funkygoby/documents/musique/partitions"
OUTPUT="$ROOT/output"
INCLUDE="$ROOT/include"
EXCLUDE="$ROOT/exclude"
#Build variables, --output is set at runtime for each file processed because we can't modify OUTPUT from subshell
LILYPOND="lilypond"
OPT="--include=$INCLUDE"
LILYPOND_EXT="ly"
#Scope: toutes les variables sont globales sauf si déclarées local ainsi que $@ $1 etc qui sont propres à la fonction appelée. Quelle bordel.
#Ici $1 désigne le premier argument passé à la fonction (mais pas au script)
#La notation ksh function maFonction () {..} met les variables en scope local automatique mais ça n'a pas l'air très portable.
#Ok mais si la fonction est appelée dans un subshell alors elle hérite de l'env mais ne peut pas modifier les variables globales
buildFile()
{
echo "\033[01;33mProcessing File\033[01;37m $1\033[00;38m"
#Extract the interesting part of the path
tmp="$(dirname "${1#$ROOT}")"
#Pack it wit OUTPUT (double '/' but it is not important)
outputfinal="$OUTPUT/$tmp"
mkdir -p "$outputfinal"
#Extract the basename, change ly->pdf and add the outputfinal path so that we can compare modification date and find out if the source file is older than the pdf
pdf="$(basename "$1")"
pdf="${pdf%."$LILYPOND_EXT"}"
pdf="$outputfinal/$pdf.pdf"
#if $1 is newerthan $pdf
if [ "$1" -nt "$pdf" ]
then
$LILYPOND "$OPT" --output="$outputfinal" "$1"
else
echo "\033[01;32mAlready up to date\033[01;37m $1\033[00;38m"
fi
#$? is the return_status from the last command executed. We need to store it before executing anything else
local status=$?
if [ $status -eq 0 ]
then
echo "\033[01;32mDone File\033[01;37m $1\033[00;38m"
else
echo "\033[01;31mFailed File\033[01;37m $1\033[00;38m"
fi
return $status
}
buildDir()
{
#If there is \n in filename, we are screwed but I have never seen one in real life
#Another way on SO:
#export -f buildFile
#find "$1" -name "*.ly" -exec sh -c 'buidFile "$0"' {} \;
#Another way: I could do recursivity, cd into directoy until there isn't subdir anymore then fire buildFile at everything.
#The while read spawn a new shell. It inherit the env but can't modify it. So we add an exit and made sure that it is in the subshell with ()
echo "\033[01;33mProcessing Dir\033[01;37m $1\033[00;38m"
status=0
find "$1" -name "*$LILYPOND_EXT" |
( while read file
do
buildFile "$file"
#Increment by the exit status of buildFile
#status scope is bound to this while loop
status=$(($status+$?))
done
exit $status )
status=$(($status+$?))
if [ $status -eq 0 ]
then
echo "\033[01;32mDone Dir\033[01;37m $1\033[00;38m"
else
echo "\033[01;31mFailed Dir $status times\033[01;37m $1\033[00;38m"
fi
return $status
}
#Parse arguments. $@ is all arguments but already separated
if [ $# -eq 0 ]
then
buildDir "$PWD"
fi
for arg in "$@"
do
#readlink expand link but also ./ ~ and stuff that would mess up outputfinal
#eval isn't a solution because it would interpret rm and such
arg="$(readlink -f "$arg")"
if [ -f "$arg" ]
then
buildFile "$arg"
elif [ -d "$arg" ]
then
buildDir "$arg"
else
echo "Invalid argument $arg"
fi
done