Cyclomatic complexity script 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 &gt; /dev/null 2&gt;&amp;1
    else
        tempcounter=$(echo $line|awk '/;;$/{counter++}END{print counter}')
        let counter=$counter+$tempcounter &gt; /dev/null 2&gt;&amp;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 &lt; $filename

let counter=$counter-$casecounter &gt; /dev/null 2&gt;&amp;1
echo "Cyclomatic complexity score: $counter"
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 

    &lt;&lt;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 &lt;&lt;&lt; $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).