I'm a HPC guy so I'm all about "make it work, then make it fast." I have a little bash script which gets a Physical Source Lines Of Code (PSLOC) and Logical SLOC (currently just of Java code, but it would work for C and C++ too). It works by finding all *.java
files in a directory and then goes through each file found and uses sed to delete stuff I don't want. It first does PSLOC by deleting all comments, whitespace, and blank lines. It then gets LSLOC by taking PSLOC and deleting all lines starting with #
or @
, replacing {}()
's with a blank space, deleting whitespace, and finally deleting blank lines. I get the line count for PSLOC and LSLOC with wc -l
.
This works, but I'm a scripting noob and haven't done anything like this before. What I'm wondering is how can I make this script faster and easier to read/maintain:
#!/bin/bash
DIR="C:/dev/somejavafolder"
LANGUAGE="java"
find "$DIR" -type f -name "*.$LANGUAGE" > filelist
while read FILE; do
PSLOC="$FILE.psloc"
LSLOC="$FILE.lsloc"
# Get PSLOC
sed -e "s/\/\/.*$//" $FILE > $PSLOC # deletes all /* to */ lines
sed -i -e 's|/\*|\n&|g;s|*/|&\n|g' $PSLOC # ^
sed -i -e '/\/\*/,/*\//d' $PSLOC # deletes all // lines
sed -i -e 's/^[ \t]*//' -e 's/[ \t]*$//' $PSLOC # deletes all whitespace
sed -i -e '/^$/d' $PSLOC # deletes all blank lines
wc -l $PSLOC
# Get LSLOC
sed -e 's/#.*//' $PSLOC > $LSLOC # deletes all # lines
sed -i -e 's/@.*//' $LSLOC # deletes all # lines
sed -i -e 's/{/ /g;s/}/ /g;s/)/ /g;s/(/ /g' $LSLOC # replaces {}() with spaces
sed -i -e 's/^[ \t]*//' -e 's/[ \t]*$//' $LSLOC # deletes all whitespace
sed -i -e '/^$/d' $LSLOC # deletes all blank lines
wc -l $LSLOC
done < filelist
find "$DIR" -name "*.*sloc" -type f -delete
This gets PSLOC and LSLOC counts of 531 java files (~80524 lines) in about 4 minutes.
Edit: per @choroba's suggestion, I changed the file to
#!/bin/bash
DIR="C:/dev/somejavafolder"
LANGUAGE="java"
find "$DIR" -type f -name "*.$LANGUAGE" > filelist
while read FILE; do
PSLOC="$FILE.psloc"
LSLOC="$FILE.lsloc"
sed -e "s/\/\/.*$//" -e 's|/\*|\n&|g;s|*/|&\n|g' -e '/\/\*/,/*\//d' -e 's/^[ \t]*//' -e 's/[ \t]*$//' -e '/^$/d' $FILE > $PSLOC # deletes all /* to */ lines
wc -l $PSLOC
sed -e 's/#.*//' -e 's/@.*//' -e 's/{/ /g;s/}/ /g;s/)/ /g;s/(/ /g' -e 's/^[ \t]*//' -e 's/[ \t]*$//' -e '/^$/d' $PSLOC > $LSLOC # deletes all # lines
wc -l $LSLOC
done < filelist
find "$DIR" -name "*.*sloc" -type f -delete
and the runtime went down to about 80 seconds. Awesome! Any other ideas?
Edit 2: So now it's even more succinct without writing the changes to files, just spitting out the linecount. How would I go about spitting the two numbers resulting from sed ... | wc -l
on the same line separated by a space without doing some assignment like a=($(sed ... | wc -l))
and using echo
?
#!/bin/bash
DIR="C:/dev/somejavafolder"
LANGUAGE="java"
wc -l `find $DIR -type f -name '*.java'` > OSLOC
sed -i -e "s/^[ \t]*//" -e "s/[ \t]*$//" OSLOC
find "$DIR" -type f -name "*.$LANGUAGE" > filelist
while read FILE; do
# Get PSLOC
sed -e "s/\/\/.*$//" -e "s|/\*|\n&|g;s|*/|&\n|g" -e "/\/\*/,/*\//d" -e "s/^[ \t]*//" -e "s/[ \t]*$//" -e "/^$/d" $FILE | wc -l
# Get LSLOC
sed -e "s/\/\/.*$//" -e "s|/\*|\n&|g;s|*/|&\n|g" -e "/\/\*/,/*\//d" -e "s/#.*//" -e "s/@.*//" -e "s/{/ /g;s/}/ /g;s/)/ /g;s/(/ /g" -e "s/^[ \t]*//" -e "s/[ \t]*$//" -e "/^$/d" $FILE | wc -l
done < filelist
find "$DIR" -name "*.*sloc" -type f -delete
-e
's of one sed call? \$\endgroup\$wc -l
with the single line sed calls so that it just counts what the file would be without actually writing it? \$\endgroup\$> file
do| wc -l
. \$\endgroup\$#
or@
. \$\endgroup\$