Handy tool.
One proviso: for every function definition, of the form
thisFunction()
there must be a corresponding closing brace, of the form
} # thisFunction()
Sample report without "--details":
CALL to promptForReferencePath [selectOneOf]
CALL to purgeIdentical [reportAndPurge]
CALL to purgeIdentical [reportAndPurge]
CALL to purgeIdentical [reportAndPurge]
CALL to purgeIdentical [reportAndPurge]
CALL to diffFile [diffFile]
CALL to diffImg [diffFile]
CALL to diffVid [diffFile]
CALL to diffBin [diffFile]
CALL to diffFile [fils_NewerInReference]
CALL to closeMatchDiff [fils_NewerInReference]
CALL to closeMatchDiff [fils_NewerInReference]
CALL to closeMatchDiff [fils_NewerInReference]
CALL to closeMatchDiff [fils_NewerInReference]
CALL to reportAndPurge [fils_NewerInReference]
CALL to reportAndPurge [fils_NewerInReference]
CALL to diffFile [fils_NewerInCurrent]
CALL to closeMatchDiff [fils_NewerInCurrent]
CALL to closeMatchDiff [fils_NewerInCurrent]
CALL to closeMatchDiff [fils_NewerInCurrent]
CALL to closeMatchDiff [fils_NewerInCurrent]
CALL to reportAndPurge [fils_NewerInCurrent]
CALL to reportAndPurge [fils_NewerInCurrent]
CALL to dirs_NewerInReference [evaluateDirs]
CALL to evaluateFils [evaluateDirs]
CALL to dirs_NewerInCurrent [evaluateDirs]
CALL to fils_NewerInReference [evaluateFils]
CALL to fils_NewerInCurrent [evaluateFils]
CALL to diffFile [evaluateFils]
CALL to closeMatchDiff [evaluateFils]
CALL to closeMatchDiff [evaluateFils]
CALL to reportAndPurge [evaluateFils]
CALL to fils_NewerInReference [evaluateFils]
CALL to fils_NewerInCurrent [evaluateFils]
CALL to evaluateDirs [scanLibsLoop]
CALL to evaluateFils [scanLibsLoop]
CALL to promptForReferencePath [MAIN]
CALL to scanLibsLoop [MAIN]
CALL to selectOneOf [MAIN]
CALL to scanLibsLoop [MAIN]
CALL to selectYesNo [MAIN]
Sample report with the "--details" option:
Function selectYesNo -> funcName[1]
Function promptForReferencePath -> funcName[2]
Function selectOneOf -> funcName[3]
Function dirs_NewerInReference -> funcName[4]
Function dirs_NewerInCurrent -> funcName[5]
Function purgeIdentical -> funcName[6]
Function reportAndPurge -> funcName[7]
Function diffImg -> funcName[8]
Function diffVid -> funcName[9]
Function diffBin -> funcName[10]
Function closeMatchDiff -> funcName[11]
Function diffFile -> funcName[12]
Function fils_NewerInReference -> funcName[13]
Function fils_NewerInCurrent -> funcName[14]
Function evaluateDirs -> funcName[15]
Function evaluateFils -> funcName[16]
Function scanLibsLoop -> funcName[17]
DEFINE selectYesNo ...
END selectYesNo .
DEFINE promptForReferencePath ...
END promptForReferencePath .
DEFINE selectOneOf ...
CALL to promptForReferencePath [selectOneOf]
END selectOneOf .
DEFINE dirs_NewerInReference ...
END dirs_NewerInReference .
DEFINE dirs_NewerInCurrent ...
END dirs_NewerInCurrent .
DEFINE purgeIdentical ...
END purgeIdentical .
DEFINE reportAndPurge ...
CALL to purgeIdentical [reportAndPurge]
CALL to purgeIdentical [reportAndPurge]
CALL to purgeIdentical [reportAndPurge]
CALL to purgeIdentical [reportAndPurge]
END reportAndPurge .
DEFINE diffImg ...
END diffImg .
DEFINE diffVid ...
END diffVid .
DEFINE diffBin ...
END diffBin .
DEFINE closeMatchDiff ...
END closeMatchDiff .
DEFINE diffFile ...
CALL to diffFile [diffFile]
CALL to diffImg [diffFile]
CALL to diffVid [diffFile]
CALL to diffBin [diffFile]
END diffFile .
DEFINE fils_NewerInReference ...
CALL to diffFile [fils_NewerInReference]
CALL to closeMatchDiff [fils_NewerInReference]
CALL to closeMatchDiff [fils_NewerInReference]
CALL to closeMatchDiff [fils_NewerInReference]
CALL to closeMatchDiff [fils_NewerInReference]
CALL to reportAndPurge [fils_NewerInReference]
CALL to reportAndPurge [fils_NewerInReference]
END fils_NewerInReference .
DEFINE fils_NewerInCurrent ...
CALL to diffFile [fils_NewerInCurrent]
CALL to closeMatchDiff [fils_NewerInCurrent]
CALL to closeMatchDiff [fils_NewerInCurrent]
CALL to closeMatchDiff [fils_NewerInCurrent]
CALL to closeMatchDiff [fils_NewerInCurrent]
CALL to reportAndPurge [fils_NewerInCurrent]
CALL to reportAndPurge [fils_NewerInCurrent]
END fils_NewerInCurrent .
DEFINE evaluateDirs ...
CALL to dirs_NewerInReference [evaluateDirs]
CALL to evaluateFils [evaluateDirs]
CALL to dirs_NewerInCurrent [evaluateDirs]
END evaluateDirs .
DEFINE evaluateFils ...
CALL to fils_NewerInReference [evaluateFils]
CALL to fils_NewerInCurrent [evaluateFils]
CALL to diffFile [evaluateFils]
CALL to closeMatchDiff [evaluateFils]
CALL to closeMatchDiff [evaluateFils]
CALL to reportAndPurge [evaluateFils]
CALL to fils_NewerInReference [evaluateFils]
CALL to fils_NewerInCurrent [evaluateFils]
END evaluateFils .
DEFINE scanLibsLoop ...
CALL to evaluateDirs [scanLibsLoop]
CALL to evaluateFils [scanLibsLoop]
END scanLibsLoop .
[MAIN] CALL to promptForReferencePath
[MAIN] CALL to scanLibsLoop
[MAIN] CALL to selectOneOf
[MAIN] CALL to scanLibsLoop
[MAIN] CALL to selectYesNo
Script:
#!/bin/bash
#23456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+
####################################################################################################
###
### Script for analyzing bash scripts, identify functions defined, and report where functions called.
###
### MOTIVATION: Analysis and documentation of the script COMPARE+PURGE.sh
###
####################################################################################################
doVerb=0
while [ $# -gt 0 ]
do
case "$1" in
"--details" ) doVerb=1 ; shift ;;
"--file" ) input="${2}" ; shift ; shift ;;
* ) printf "\n\t ERROR: Bad option used on command line. Only valid options: [ --details ] --file {filename} \n Bye!\n\n" ; exit 1 ;;
esac
done
test -n "${input}" || { printf "\n\t ERROR: Must provide filename as input parameter!\n Bye!\n\n" ; exit 1 ; }
test -s "${input}" || { printf "\n\t ERROR: Inapropriate attempt to analyze empty input file!\n Bye!\n\n" ; exit 1 ; }
awk -v dbg="${doVerb}" -v fil="${input}" 'BEGIN{
gotFunc=0 ;
split("", funcName ) ;
funcIndx=0 ;
abandon=0 ;
do{
if( ( getline aLine <fil ) > 0 ){
pos=index( aLine, "()" ) ;
if( pos > 0 ){
#print aLine | "cat 1>&2" ;
if( aLine !~ "#"funcName[funcIndx] ){
gotFunc=1 ;
funcIndx++ ;
funcName[funcIndx]=substr( aLine, 1, pos-1 ) ;
if( dbg == 1 ){ printf(" Function %-30s -> funcName[%d]\n", funcName[funcIndx], funcIndx ) | "cat 1>&2" ; } ;
} ;
} ;
#abandon=0 ;
}else{
abandon=1 ;
} ;
}while( abandon == 0 ) ;
if( dbg == 1 ){ print "" ; } ;
gotFunc=0 ;
}{
for( i=1 ; i<= funcIndx ; i++ ){
posN=index( $0, funcName[i] ) ;
posF=index( $0, "()" ) ;
if( posN > 0 && posF == 0 ){
if( gotFunc == 1 ){
#printf("\t\t [%s] CALL to %s\n", funcThis, funcName[i] ) ;
if( dbg == 1 ){
printf("\t CALL to %-30s [%s]\n", funcName[i], funcThis ) ;
}else{
printf(" CALL to %-30s [%s]\n", funcName[i], funcThis ) ;
} ;
}else{
if( dbg == 1 ){
printf(" [MAIN] CALL to %s\n", funcName[i] ) ;
}else{
printf(" CALL to %-30s [MAIN]\n", funcName[i] ) ;
} ;
} ;
} ;
} ;
pos=index( $0, "()" ) ;
if( pos > 0 ){
if( $2 ~ /^#/ ){
gotFunc=0 ;
pos=index( $0, "#" ) ;
rem=substr( $0, pos+1 ) ;
gsub( /[(][)]/, "", rem ) ;
if( dbg == 1 ){ printf(" END %s .\n\n", rem ) | "cat 1>&2" ; } ;
}else{
gotFunc=1 ;
funcThis=substr( $0, 1, pos-1 ) ;
if( dbg == 1 ){ printf(" DEFINE %s ...\n", funcThis ) | "cat 1>&2" ; } ;
} ;
} ;
#}END{
}' <"${input}"
Enjoy!