3
\$\begingroup\$

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"
asked May 11, 2017 at 15:00
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$
  • bash grammar is notoriously hard to parse. Your script is easily misled by many valid bash 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 invoke awk for a single line of input.

  • bash is also a very powerful programming language. The constructs like

     temp=`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).

answered May 11, 2017 at 17:59
\$\endgroup\$
1
  • \$\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\$ Commented May 11, 2017 at 22:06

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.