This isn't a question--its a solution! Below is a script that I wrote for my own script file development which does what the title says. Its the closest that you can get to compiling what are otherwise purely interpreted script files. I offer it here simply for the benefit of anyone else writing script files. (That, plus I would like feedback if anything is wrong or suboptimal with it.)
Personally, I do not understand why sh even has the -n option: I would have thought that it should always be done. It gives you more safety, detecting known problems before damage can be done. Was it made an option because in the stone ages when unix was written, computers were so slow that automatic syntax checking would have been a major performance impact?
Regardless, here's the script:
#!/bin/sh
set -e # exit on first failed command
set -u # exit if encounter never set variable
#--------------------------------------------------
# Checks the syntax of either a single shell script file,
# or every shell script file in a directory,
# or every shell script file in an entire directory tree.
#
# Usage:
# sh checkSyntax.sh [-p path] [-R]
#
# -p if supplied, then requires a value that is either the path to a single .sh file
# or the path to a directory;
# if omitted, then the current working directory will be searched
#
# -R if supplied, and if the path to be searched is a directory,
# then also searches subdirectories for .sh files
#
# +++ KNOWN BUGS:
# --the find/while loop below screws up if any element in a path contains leading whitespace
#
# +++ In the future:
# --there are many more options from the find command (e.g. how symbolic links are handled--currently they are not followed)
# which might want to expose as command line arguments of this script.
#--------------------------------------------------
# Initialize option variables to preclude inheriting values from the environment:
opt_p="./" # the path to search for the find command used below; default value is the current working directory
opt_R="-maxdepth 1" # the maxdepth option for the find command used below; default value limit the search to just path itself (i.e. do not drill down into subdirectories)
# Parse command-line options:
while getopts 'p:R' option
do
case "$option" in
"p")
opt_p="$OPTARG"
;;
"R")
opt_R=""
;;
?)
echo "Script will now quit with an exit code of 1" # Note: if supply an invalid option, getopts should have already printed an error line by this point, so no need to duplicate it
exit 1
;;
esac
done
# Find all the relevant .sh files and check their syntax:
find $opt_p $opt_R -type f -iname "*.sh" | while read shFile # see Note below...
do
( sh -n "$shFile" ) # sh -n will merely syntax check (never execute) shFile, and will print to stdout any errors found; encase in parentheses to execute in a subshell so that if a syntax error is found, which causes sh -n to have a non-zero exit code, then this parent shell does not see it; critical since it will stop executing (due to the set -e line at the top of the file) otherwise
done
# Note: originaly used this line instead of the first line above:
# for shFile in `find $opt_p $opt_R -type f -iname "*.sh"` # to understand this line, execute "man find"; was inspired by this script: Bounty: A recursive .M3U index generating shell script | debianHELP
# Dropped it because it fails on path elements which contain whitespace.
# The solution above is discussed here: http://www.unix.com/shell-programming-scripting/27487-how-read-filenames-space-between.html