Validate input from bash - linux

I need to make a bash script that takes user input and does some validation.
The input is small. I just need to get a user choice "yes" or "no" and a file path and make sure the input is indeed a filepath and that he entered yes or no.
How can I validate the user input?

from your bash prompt type
help read
example:
step 1.
pete#ruthie ~ $ read -p "Yes or No :" ANSWER
Yes or No :Yes
pete#ruthie ~ $ echo $ANSWER
Yes
step 2.
case $ANSWER in
Y* | y* ) echo "ANSWER is yes" ;;
N* | n*) echo ANSWER is no;;
*) echo "Unclear Response" ;;
esac
ANSWER is yes
Or all on one line:
case $ANSWER in Y* | y* ) echo "ANSWER is yes" ;; N* | n*) echo ANSWER is no;; *) echo "Unclear Response" ;; esac
ANSWER is yes
So something vaguely like::
read -p "Enter File Name :" FILE_TO_CHECK
if [ ! -e "$FILE_TO_CHECK" ]; then
echo -e "There is no file called $FILE_TO_CHECK \n please check spelling"
else
echo "Congratulations found $FILE_TO_CHECK. "
ls -lah $FILE_TO_CHECK
read -p "Process $FILE_TO_CHECK ? Y or N : PROCEED
case $PROCEED in
Y* | y*)
##call function or system command to do something with this data
foo $FILE_TO_CHECK
;;
* )
echo -e "Note $USER\n No action taken by $0 on $FILE_TO_CHECK"
;;
esac
fi
The test command is your friend. You really need to learn its ways ASAP.
help [
and
help test
The "help bashthing" command is a great quick reminder.
see also: the Advanced Bash Scripting Guide (ABS)
http://tldp.org/LDP/abs/html/
FWIW:
This is a very simple example and wont accommodate much in the way of unexpected user input.
I would probably call functions for each step of this as then I could conditionally represent the user with another chance.
The case tests nested like this is plain ugly :) === hard to get neat and read later
There are other ways to achieve all this try zenity for a GTK style GUI.
The A.B.S is excellent but dont try to eat it all at once !

Related

bash input/ouput to a log file

I have a bash script with below statement. I need to output both the question "Are you happy? and the answer "yes" or "no" to a log file. I'm able to get the answer to log file with "echo "$yn" >>log1.log 2>&1" but not the question (read command)
while true; do
read -p "Are you happy? " yn
case $yn in
[Yy]* ) break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done
echo "$yn" >>log1.log 2>&1
What I have tried:
read -p "Are you okay?" yn > log.log 2>&1
This will indeed work, however when I run my scrip the question is not displayed.
The only way I found is to echo "Are you happy? $yn" >>log1.log 2>&1
The problem with this is that I have several prompts with long statements, I like to keep my scripts nice and short
The redirect only applies to the echo. You need to add a similar redirect afther the done. (This will also redirect the error message which you echo from within case, which incidentally should go to standard error):
while true; do
read -p "Are you happy? " yn
case $yn in
[Yy]* ) break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no." >&2;;
esac
done >>log1.log 2>&1
echo "$yn" >>log1.log
or put the entire sequence of commands inside parenthes or braces.
{ while true; do
read -p "Are you happy? " yn
case $yn in
[Yy]* ) break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no." >&2;;
esac
done
echo "$yn"; } >>log1.log 2>&1
Like you discovered, and like the term should reveal, a redirection causes the output to go to another place (so a file instead of your terminal, in this case). If you want it displayed on the terminal as well, tee does that, but it's probably not suitable for interactive scripts (because you can't include standard input in the output).
tripleee$ cat >nst
#!/bin/bash
while true; do
read -p "Are you happy? " yn
case $yn in
[Yy]*) break;;
[Nn]*) exit;;
*) echo "Please answer yes or no." >&2;;
esac
done
echo "$yn"
^D
tripleee$ chmod +x ./nst
tripleee$ ./nst 2>&1 | tee log1.log
Are you happy? forget it
Please answer yes or no.
Are you happy? no
tripleee$ cat log1.log
Are you happy? Please answer yes or no.
Are you happy? tripleee$
Perhaps you are actually looking for the script command?
tripleee$ script typescript ./nst
Script started, output file is typescript
Are you happy? forget it
Please answer yes or no.
Are you happy? yes ma'am
yes ma'am
Script done, output file is typescript
tripleee$ nl -ba typescript
1 Script started on Thu Oct 26 20:34:04 2017
2 command: ./nst
3 Are you happy? forget it
4 Please answer yes or no.
5 Are you happy? yes ma'am
6 yes ma'am
7
8 Script done on Thu Oct 26 20:34:11 2017
Note that the typescript file will contain any edits done by the user, i.e. typos and their correction will be included, with the various display codes used by the terminal to execute the edits.

I Can't get my bash code right to check an answer read in

My current script is like this but is for one answer.
A1="hp"
echo
echo 1.What os uses swinstall for software installs?
read answer
if [ "$answer == "$A1" ]; then
echo "correct"
else
echo "incorrect"
fi
(for questions with two answers)i would like to put in place of this see below i would like for them to be able to choose either answer up top though.
echo the answer could be hp or ux
What about using a case statement? This allows for a bit of flexibility:
A1="hp"
A2="ux"
echo
echo 1. What OS uses swinstall for software installs?
read answer
case $answer in
($A1) echo "correct"
;;
($A2) echo "correct"
;;
(*) echo "incorrect"
;;
esac

Bash how check if an argument is a sed patterns?

I'm working on a bash script that should receive many arguments. those arguments could be simple like:
Myscript [-r] [-l|-u] <dir/file names...>
or it could be just a sed patterns given instead of a simple arguments like:
Myscript <sed pattern> <dir/file names...>
So my question is how to check if the arguments given is a sed patterns and not directory path or filename?
I have done something but it doesn't work for a long path (dir1/dir2/dir)
while [[ $# > 0 ]]
do
key="$1"
case $key in
-ru|-ur)
config_recusive
config_upper
shift
;;
-rl|-lr)
config_recusive
config_lower
shift
;;
-r)
config_recusive
shift
;;
-l)
config_lower
shift
;;
-u)
config_upper
shift
;;
*)
check="^.*/.*/.*$"
if [[ $key =~ $check ]] ; then
config_sed_pattern
else
TARGET=$key
fi
shift
;;
esac
done
To be more clear, here is an example of my problem when I'm trying to run the script like that:
./myscript -u tmp/users/regions
its being confused taking the path (tmp/users/regions) as a sed patterns.
hope that I was clear enough.
waiting for your help :)
thanks
i think you could use try run that script on something
echo a | sed "$pattern" >/dev/null 2>/dev/null
and then check the $?
if [ $? != 0 ]; then # means sed failed
#...
but maybe sed will think it will be its argument too.
btw, handle arguments using getopt will make things easier.
i don't think the way you design argument format is good. maybe you think making arguments in different forms could make your program looks dynamic and awesome. but now you are trying to solve a problem have nothing to do with your serious business and wasting your own time and ours. that's not good. it's better to make things stupid and clear.
in your case, maybe you can add another argument to show it's a sed expression that follows.
YourScript -e <sed expr> <others>
and in such a way, you will have idea about what you have done two weeks later.

I keep getting a 'while syntax' error on the output of the at job in unix and I have no idea why

#!/usr/dt/bin/dtksh
while getopts w:m: option
do
case $option in
w) wflag=1
wval="$OPTARG";;
m) mflag=1
mval="$OPTARG";;
?) printf 'BAD\n' $0
exit 2;;
esac
done
if [ ! -z "$wflag" ]; then
printf "W and -w arg is $wval\n"
fi
if [ ! -z "$mflag" ]; then
printf "M and -m arg is $mval\n"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: $* \n"
at $wval <<ENDMARKER
echo $* >> Search_List
tr " " "\n" <Search_List >Usr_List
while true; do
if [ -s Usr_List ]; then
for i in $(cat Usr_List); do
if finger -m | grep $i; then
echo '$i is online' | elm user
sed '/$i/d' <Usr_List >tmplist
mv tmplist Usr_List
fi
done
else
break
fi
done
ENDMARKER
Essentially I want to keep searching through until it is empty. Each time an element of the list is found, it is deleted. Once the list is empty quit.
There are no error messages when I first run the command, it only shows up in an email containing the output of the at job.
Thanks in advance for any advice
EDIT: The script uses getopts and takes one argument for -w and one for -m, the w value is set as the time for the at job, the m still has to be used. Any arguments after the one for m are sent to a file called Search_List, Search_List is edited and saved as Usr_List. Then in the while loop, while Usr_List is not empty, the script checks the results of finger -m against the names in Usr_List. If a name is found, it is removed from Usr_List. Once Usr_List is empty, the program should stop.
elm is a way to send an email, so elm user sends an email to user.
The error is :
while: Expression syntax
at uses /bin/sh by default.
at now <<ENDMARKER
<code here>
ENDMARKER
All of this executes under /bin/sh, which on some systems can be Bourne Shell (Solaris for example).
You need to figure out what /bin/sh is for your system, then modify things accordingly. Plus, read the gurantees about what is and what is not in your "at" environment. I think the problem lies there. You have both UNIX and linux tags. So I cannot give a lot more help than that.
You can enable logging -- the way YOU need it -- of the at code chunk:
exec 2&>1 > /tmp/somefile.log
Then write debugging messages to stdout or stderr.
Your HEREDOC is being interpolated. Try quoting the delimiter:
at $wval << 'ENDMARKER'
Although ( I haven't looked closely) it appears that you want some interpolation. But you definitely do not want it on the line in which you reference $i, so quote that $ if you do not quote the entire heredoc:
if finger -m | grep \$i; then
You need to pass the -k option to at:
...
at -k $wval <<ENDMARKER
...
at is otherwise defaulting to your login shell which is csh or one of its derivatives.
It turns out that the while command and the if command needed to be combined.
while [[ -s Usr_List ]]; do
......
done

Issue controlling script flow

I'm new to shell scripting, my script appears to be okay, but its the flow that I'm having an issue controlling. Could someone point out what silly mistake I've made please.
#! /bin/sh
echo "Are you sure youx want to delete $1? Answer y or n"
read ans
echo $ans
if $ans = "y"|"Y"
then
mv $1 /home/parallels/dustbin
echo "File $1 has been deleted"
else echo "File $1 has not been deleted"
fi
Make your if condition like this:
if [ "$ans" = "y" -o "$ans" = "Y" ]
There are a few things wrong with your script. Some are serious, some are less so.
First, the serious problems.
As guru suggested, you need to use square brackets to surround your if condition. This is because if only tests for the output of a condition, it doesn't perform actual string comparisons. Traditionally, a program called /bin/test, which was also called /bin/[ took care of that. These days, that functionality is built in to the shell, but /bin/sh still behaves as if it's a separate program.
In fact, you can do interesting things with if when you don't use square brackets for your condition. For example, if grep -q 'RE' /path/to/file; then is quite common. The grep -q command issues no output, but simply returns a "success" or "fail" that is detected by if.
Second serious problem is that you are echoing a status that may or may not be true. I call this a serious problem because ... well, log messages simply shouldn't make false claims. If the permissions are wrong for the file in $1, or the filename contains a space, then your mv command will fail, but the message will claim that it did not. More on this later.
Next, the less serious problems.
These are mostly style and optimization things.
First off, read on most platforms includes a -p option that lets you specify a prompt. Use this, and you don't need to include an echo command.
Second, your indenting makes it hard to see what the if construct is wrapping. This isn't a huge problem in a program this small, but as you grow, you REALLY want to follow consistent standards.
Third, you can probably get more flexibility in multiple-choice questions like this if you use case statements instead of if.
After all that, here's how I'd write this script:
#!/bin/sh
if [ "$1" = "-y" ]; then
ans=y
shift
elif [ -t 0 ]; then
read -p "Are you sure you want to delete '$1' (y/N) ? " ans
fi
case "$ans" in
Y*|y*)
retval=0
if [ -z "$1" ]; then
retval=64
echo "ERROR: you didn't specify a filename." >&2
if [ ! -f "$1" ]; then
retval=66
echo "ERROR: file '$1' not found!" >&2
elif mv "$1" /home/parallels/dustbin/; then
echo "File '$1' has been deleted" >&2
else
retval=$?
echo "ERROR: file '$1' could not be deleted!" >&2
fi
;;
*)
echo "ABORT: file '$1' has not been deleted" >&2
retval=4
;;
esac
exit $retval
Aside from what's mentioned above, here are some things in this code snippet:
[ "$1" = "-y" ] - if the user specifies a -y option, then we behave as if the question was answered with a "yes".
[ -t 0 ] - this tests whether we are on an interactive terminal. If we are, then it makes sense to ask questions with read.
Y*|y*) - in a case statement, this matches any string that begins with an upper or lower case "y". Valid affirmative responses would therefore be "Y", "yes", "yellow", etc.
[ ! -f "$1" ] - this tests whether the file exists. You can man test or man sh to see the various tests available in shell. (-f may not be the most appropriate for you.)
>&2 - at the end of a line, sends its output to "standard error" instead of "standard out". This changes how output will be handled by pipes, cron, etc. Errors and log data are often sent to stderr, so that stdout can be dedicated to a program's actual output.
mv "$1" ... - The filename is in quotes. This protects you in case the filename has special characters like spaces in it.
$retval - the values for this came from a best guess of the closest item in man sysexits.
retval=$? - this is the exit status of the most recently executed command. In this case, that means we're assigning mv's exit status to the variable $retval, so that if mv failed, the whole script reports the reason for the fail, as far as mv is concerned.
You can also convert the user response to either case and just check it for respective case like
read ans
ans=${ans,,} # make 'ans' lowercase, or use ${ans^^} for making it uppercase
if [ "$ans" = "y" ]
then
....
fi
Below is the perfect code with error handling included
#!/bin/sh
echo "Are you sure you want to delete $1? Answer y or n"
read ans
echo $ans
if [ $ans == "y" ] || [ $ans == "Y" ]
then
if [ -f $1 ]
then
mv $1 /home/parallels/dustbin
echo "File $1 has been deleted"
else
echo " File $1 is not found"
fi
else
echo "File $1 has not been deleted"
fi

Resources