4
\$\begingroup\$

I'm writing a bash script to search C source files and find the lines where I missed the void keyword (unlike in C++, in C, empty parameter lists have to be indicated with the void keyword).

I read that I shouldn't use for but while: https://mywiki.wooledge.org/DontReadLinesWithFor

I find that for has an easier to follow, top-down structure (first I write the command that produces the items to be iterated, then I write what to do with each).

I understand that if a filename contained a newline it would cause trouble in case of for; however, with IFS=$'\n'; set -f I would think all other problems are solved.

My code using for:

#!/bin/bash

returnTypeOpt_BRE='\([a-zA-Z_][a-zA-Z0-9_]\{0,\}[*  ]\{1,\}\)\{0,1\}'
spaceOpt_BRE=$'[ \t\r]\\{0,\\}'
functionName_BRE='\([a-zA-Z_][a-zA-Z0-9_]\{0,\}\)'
declarationMissingVoid_BRE="^$returnTypeOpt_BRE$spaceOpt_BRE$functionName_BRE$spaceOpt_BRE($spaceOpt_BRE)$spaceOpt_BRE;$spaceOpt_BRE\$"

targetFolder_Path="$1"
if [ $# -eq 0 ]; then
    targetFolder_Path="."
fi

IFS=$'\n'; set -f; for file in $( find "$targetFolder_Path" -name '*.c' -or -name '*.h' ); do
    printf "%s\n" "$file"
    for line in $( grep --basic-regex --regexp=$declarationMissingVoid_BRE $file ); do
        printf "\t>%s\n" "$line"
    done
done; unset IFS; set +f

My code using while:

#!/bin/bash

returnTypeOpt_BRE='\([a-zA-Z_][a-zA-Z0-9_]\{0,\}[*  ]\{1,\}\)\{0,1\}'
spaceOpt_BRE=$'[ \t\r]\\{0,\\}'
functionName_BRE='\([a-zA-Z_][a-zA-Z0-9_]\{0,\}\)'
declarationMissingVoid_BRE="^$returnTypeOpt_BRE$spaceOpt_BRE$functionName_BRE$spaceOpt_BRE($spaceOpt_BRE)$spaceOpt_BRE;$spaceOpt_BRE\$"

targetFolder_Path="$1"
if [ $# -eq 0 ]; then
    targetFolder_Path="."
fi

while IFS=; read -u 3 -r -d $'\0' file; do
    printf "%s\n" "$file"
    while IFS=; read -u 4 -r line; do
        printf "\t>%s\n" "$line"
    done 4< <( grep --basic-regex --regexp=$declarationMissingVoid_BRE $file )
done 3< <( find "$targetFolder_Path" -name '*.c' -print0 -or -name '*.h' -print0 )

So, for me both scripts work correctly, and produce the same result.

Beyond the "filenames with newlines" issue is there anything else that would make while better? Can you show me a test case?

If I had to solve similar but different (possibly more complex) tasks would I have problems with for?

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I'm writing a bash script to search C source files and find the lines where I missed the void keyword

This alone suggests that your approach is very problematic. To do this properly you'd effectively need to write most of a C parser, and that isn't a good use of your time. There are other approaches that would get you more mileage and accuracy:

  • Double-check that your compiler (gcc?) for sure doesn't have warnings that cover your use-case; if it does, then simply compile and look at the output.
  • Failing that, reuse a parser. I recommend the Clang AST.

Bash is just the hideously wrong tool for this. Save yourself the pain of writing regexes that are both complex and likely missing many edge cases.

\$\endgroup\$
2
  • \$\begingroup\$ GCC doesn't have warnings for this. If it did, that would be great, but it doesn't. \$\endgroup\$
    – S.S. Anne
    Mar 15, 2020 at 17:25
  • \$\begingroup\$ My C source files are quite simple, and I'm not afraid of missing some edge cases. On the other hand, I would like to know how to search regex patterns in multiple files with bash scripts. \$\endgroup\$
    – z32a7ul
    Mar 15, 2020 at 18:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.