View Single Post
  #8   (View Single Post)  
Old 25th March 2018
Funkygoby Funkygoby is offline
Fdisk Soldier
 
Join Date: Aug 2015
Posts: 57
Default

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
Reply With Quote