Problem:
I am writing a script that allows user to key in a directory. Example: /root/tmp.
Since the user can key in anything he/she wants, I need to validate to make sure that he/she does not key in anything funny i.e. #@!*&^$><,."';:}[]+=)(|\%~`
Only "/", "-", "_", alphabet and numeric are the valid characters.
Initially, I wrote the code as follows but it does not work for certain special characters such as ")", "(", "\" and "`" (there could be more that I may have missed). Basically, it complained that the script had syntax error.
case "$b" in
*@) echo "Error: It has @";;
*~) echo "Error: It has ~";;
*#*) echo "Error: It has #";;
*\**) echo "Error: It has *";;
*) echo "Directory is ok";;
esac
I believe there is a better way to code than listing everything in a case statement.
Hope someone can suggest a better solution. Thanks.
while :
do
echo "Please enter the directory: \c"
read b
if [ "$b" = "" ]; then
echo "ERROR: Directory should not be blank!"
else
# Validation: more condition is required. This is an area I need help
case "$b" in
*@) echo "Error: It has @";;
*~) echo "Error: It has ~";;
*#*) echo "Error: It has #";;
*\**) echo "Error: It has *";;
*) echo "Directory is ok"
break;;
esac
fi
done
I have thought about it too but my skill in Bourne shell is limited and I could not figure out how I can do it. I have the following codes on my mind but I could not code something that is good enough to prevent the unwanted special characters from being entered by user:
# This will prevent user from entering /, _ & - which should be allowed
echo $b | grep "[^0-9]" > /dev/null 2>&1
if [ "$?" -eq "0" ]; then
echo "ERROR: Invalid Directory!"
fi
# This will prevent user from entering /, _ & - which should be allowed
echo $b | grep "[^a-z]" > /dev/null 2>&1
if [ "$?" -eq "0" ]; then
echo "ERROR: Invalid Directory!"
fi
# This will prevent user from entering /, _ & - which should be allowed
echo $b | grep "[^A-Z]" > /dev/null 2>&1
if [ "$?" -eq "0" ]; then
echo "ERROR: Invalid Directory!"
fi
I wrote the code as follows. I purposely create an infinite loop to re-enter the directory:
while :
do
echo "Enter directory: \c"
read b
case $b in
*[!/-_A-Za-z0-9]*) echo "Error" >&2;; [!/-_A-Za-z0-9]) echo "Error 1" >&2;; # For experiment only
[!/-_A-Za-z0-9]) echo "Error 2" >&2;; # For experiment only
[!/-_A-Za-z0-9]) echo "Error 3" >&2;; # For experiment only
esac
done
The result looks unpredictible to me. The ones with wrong result is indicated by "[Wrong]". They either fail to detect special character or treat valid ones as invalid. See the output below:
Enter directory: 12@ [Wrong]
Enter directory: 12@12 [Wrong]
Enter directory: 12#3]
Error
Enter directory: 12`76
Error
Enter directory: 12\87 [Wrong]
Enter directory: 12/778
Enter directory: 12%87
Error
Enter directory: 67)
Error
Enter directory: *(&*
Error
Enter directory: !76h
Error
Enter directory: (12
Error
Enter directory: `344
Error
Enter directory: 788'
Error
Enter directory: 87"+=
Error
Enter directory: 12_767
Enter directory: 788- [Wrong]
Error
Enter directory: 67-878 [Wrong]
Error
Enter directory: :78 [Wrong]
Enter directory: 878; [Wrong]
Enter directory: gha.js
Error
Enter directory: hg.
Error
Enter directory: |78
Error
Enter directory: {98}^H
Error
Era, there's no need to apologise. You're just trying to help. I would go astray anyway while trying to find my way to the solution It would probably be worse if I am doing on my own without any help from you. I will update you again after trying your new suggestion. Thanks
Era, I have just remembered the problem of having an individual character in each case. I cannot key in cases as follows because I encountered syntax errors:
*`*) echo "Error";;
*\*) echo "Error";;
*)*) echo "Error";
*(*) echo "Error";
I tried adding "\" in front of each of the character, it failed too. I tried with *\`* but it failed to detect string with 678\989
Yes, you need to backslash-escape (or quote) any characters with special meaning.
Backslash in particular is special when reading too, but if you can just get the others to work, that's a start.
*\`* should match 678`989 not 678\989
Merely quoting closing parenthesis is insufficient, because it has a special meaning in the syntax of the case statement, and quoting won't help the parser, so you need the backslash specifically.
case $b in
*\\*) echo backslash;; # don't necessarily expect this to work
*\)*) echo close paren;; # can't just quote this one, either
*'('*) echo open paren;;
*'`'*) echo backtick;;
esac
All of those should work IMHO, but $b might not contain a backslash when you expect it to. Consider:
vnix$ echo 'foo\bar'
foo\bar
vnix$ echo 'foo\bar' | while read b; do
> set | grep b=
> done
b=foobar
bash205b='3.2.25(1)-release'
See? The backslash is parsed already by read. My bash has an option read -r to disable this but I don't know if that's portable.
If anyone has a better solution, please let me know. I am still desperate for a solution. I regret for agreeing to use Bourne shell to develop this simple application.