You really shouldn't use sh (any variant, from plain old sh to bash or ksh or zsh) to do any but the most trivial string or text processing. See Why is using a shell loop to process text considered bad practice? for some of the reasons why.
Looping over every character of a multi-line string is something that definitely shouldn't be done in shell alone. It'll be horrendously slow, and you'll run into all sorts of issues with quoting and end-of-line markers (like \n
), and most versions of sh (except for bash, ksh, and zsh) don't even have built-in support for regular expressions - which is, IMO, minimum required functionality for text processing.
Instead, use awk
or perl
or some other text-processing utility/language.
For example, you could replace your entire for loop with:
echo "$string" | perl -lne 'BEGIN {$c="Yes"; $ec=0};
if (!m/^[ #]+$/) { $c="No"; $ec=1; last };
END {print $c; exit $ec}'
or
echo "$string" | awk -v c="Yes" -v ec=0 \
'!/^[ #]*$/ { c="No"; ec=1; nextfile };
END { print c; exit ec}'
This requires GNU awk (or some other awk that supports nextfile
). On other versions, of awk, the following will work - but a tiny bit slower because it doesn't immediately exit the loop on a bad match:
echo "$string" | awk -v c="Yes" -v ec=0 \
'!/^[ #]*$/ { c="No"; ec=1 };
END { print c; exit ec}'
As should be obvious, these are all the same script, just rewritten slightly differently for the different syntaxes of perl and awk.
The regex used in both perl and awk above is /^[ #]*$/
, negated with a !
. This matches any line that doesn't contain only spaces or hashes.
All three of them return an exit code of 0 on a confirmed-good input, and 1 on a bad match. This can be tested in sh with the $?
variable:
if [ $? -eq 1 ] ; then
: # string has bad characters, do something!
fi
You could also use a simple GNU sed
script:
echo "$string" | sed -n -e '/[^ #]/q1'
if [ $? -eq 0 ] ; then echo "Yes" else echo "No" ; fi
The exit code to the q
(quit) command is a GNU extension to sed, so requires GNU sed.
Or, as @Isaac pointed out, you can use grep
's -q
(or --quiet
/--silent
) option. This suppresses normal output and only returns an exit code. For example:
echo "$string" | grep -q '[^ #]'
if [ $? -eq 0 ] ; then echo "Yes" else echo "No" ; fi
https://shellcheck.net
, a syntax checker, or installshellcheck
locally. Make usingshellcheck
part of your development process. – waltinator Apr 10 '21 at 16:35