As part of my programming training, I was told to research metrics for programming. I went ahead and turned the metrics into code starting with the cyclomatic complexity by McCabe. It works for bash sourcecode which I was targeting in this example.
My approach relies heavily on the while read line
to read the Code line by line.
I tried to use more Regex
at first but I soon hit a wall and decided to do it this way. Im familiar with Regex
but inexperienced in combining them with sed
or awk
. So I'd like to ditch the while read line
if that is possible for this kind of problem, and general readability tips for bash scripting would also be nice (im using vim to write the code).
if [ "1ドル" == "" ]
then
echo "Type in file name: "
read filename
else
# 1ドル is the first input parameter
filename=1ドル
fi
counter=1
caseflag=0
casecounter=0
while read line
do
if [ "$caseflag" == "1" ]
then
if [[ $line == *"#"* ]]
then
#takes care of the comments in the SC
temp=${line%%#*}
tempcounter=$(echo $temp|awk '/;;$/{counter++}END{print counter}')
let counter=$counter+$tempcounter > /dev/null 2>&1
else
tempcounter=$(echo $line|awk '/;;$/{counter++}END{print counter}')
let counter=$counter+$tempcounter > /dev/null 2>&1
fi
fi
#loop detection
temp=`echo $line | cut -d ' ' -f1`
if [ "$temp" == "until" ] || [ "$temp" == "if" ] || [ "$temp" == "else" ] || [ "$temp" == "elif" ] || [ "$temp" == "while" ] || [ "$temp" == "for" ]
then
let counter=$counter+1
fi
#case detection
if [ "$temp" == "case" ]
then
caseflag=1
let casecounter=$casecounter+1
elif [ "$temp" == "esac" ]
then
caseflag=0
fi
done < $filename
let counter=$counter-$casecounter > /dev/null 2>&1
echo "Cyclomatic complexity score: $counter"
1 Answer 1
bash
grammar is notoriously hard to parse. Your script is easily misled by many validbash
constructs.For example, it unconditionally consider
#
as a comment, whereas it could be escaped, or a part of a special parameter$#
, or a part of${parameter#word}
expansion.It will consider the assignment (stupid but valid)
x=\;;
as an end of case. It will miss a loop in
cat file | while read line
It will count the contents of a
<<EOF while for until EOF
as loops.
It incorrectly handles nested
case
statements.awk
is a powerful programming language. If you feel you need to invoke it, consider using it for an entire script. It is particularly inefficient to invokeawk
for a single line of input.bash
is also a very powerful programming language. The constructs liketemp=`echo $line | cut -d ' ' -f1`
can be more clearly (and more efficiently) expressed in
bash
, e.g.read temp rest <<< $line
Testing for the presence of
#
to remove the comment is redundant. The expansion works correctly even if it is not there (in which case the line remains unchanged).
-
\$\begingroup\$ Thx for pointing the errors out, I guess my inexperience with bash shines through. I think redoing it entirely with awk might be a nice idea. I'm going to try that. Thx for your time \$\endgroup\$Seitrox– Seitrox2017年05月11日 22:06:37 +00:00Commented May 11, 2017 at 22:06