I have had to write a script that can execute 2 command line arguments to execute task. In addition to a while-loop. I am having trouble with the if statement on line 16. The shell produces the following:
./asg6s: line 16: syntax error near unexpected token fi'
'/asg6s: line 16:fi
My code is as follows:
#check if number of arguments are 2
if [ $# -ne 2 ]; then
echo "Does not equal two arguments"
echo "Usage $0 inputfile outputfile"
exit 1
fi
# check if input file exists
if [ ! -e $1 ]; then
echo "$1 not found!"
exit 1
fi
#check if input file is empty
if [ ! -s $FILE ] ; then
echo "$1 is empty"
exit 1
fi
# copy contents of first file to second
cat $1 > $2
while true
do
clear
# display the menu
echo "University of Maryland."
echo "purpose of using the app"
echo -en '\n'
echo "Choose one of the following:"
echo "1 Addition"
echo "2 Subtraction"
echo "3 Multiplication"
echo "4 Division"
echo "5 Modulo"
echo "0 Exit"
echo -en '\n'
#take input for operation
read N
case $N in
1) NAME="add";OP="+";;
2) NAME="subtract";OP="-";;
3) NAME="multiply";OP="*";;
4) NAME="divide";OP="/";;
5) NAME="modulo";OP="%";;
0) echo "The progam is ending" ; exit 0;;
*) echo “Not an Acceptable entry.” ;continue;
esac
#take input numbers
echo "Enter two numbers"
read A
read B
#display value on screen and also append in the output file
echo "The operation is to $NAME. The result of $NAME $A and $B is" `expr $A $OP $B`
echo "The operation is to $NAME. The result of $NAME $A and $B is" `expr $A $OP $B` > $2
done
Any help would be appreciated.
Edit:
In the same code above I have been getting a problem with the loop statement. Well I should say the fact that I cannot get the program to print the answer for me after I input to integers into the program. Specifically, it does nothing and goes back to the point where it ask me input for what operation I want to complete. Any help would be appreciated.
Your script is almost working.
After showing the result of the expr your script continues with the while loop and calls clear. If you want to see the result, you must show the result after the clear or read a dummy key input.
Another problem is the variable $OP, that could be a *. When * is evaluated to a set of files, your expr statement will not work.
The shortest changes is adding a read statement and quoting your $OP:
echo "The operation is to $NAME. The result of $NAME $A and $B is" `expr $A "$OP" $B`
echo "The operation is to $NAME. The result of $NAME $A and $B is" `expr $A "$OP" $B` > $2
read dummy
Of course the script can be changed. Do you really want to overwrite $2 with the results or append to the file?
The 2 echo lines can be put together with tee.
I would move the clear to above the while statement and replace the last part of your script with
read A
read B
clear
#display value on screen and also append in the output file
echo "The operation is to ${NAME}. The result of ${NAME} $A and $B is $(expr $A "${OP}" $B)" | tee -a $2
echo
done
Related
Have two files:
file1 is having the key words - INFO ERROR
file2 is having the list of log files path - path1 path2
I need to exit out of the script if any of the condition in any of the loops failed.
Here is the Code:
#!/bin/bash
RC=0
while read line
do
echo "grepping from the file $line
if [ -f $line ]; then
while read key
do
echo "searching $key from the file $line
if [ condition ]; then
RC=0;
else
RC=1;
break;
fi
done < /apps/file1
else
RC=1;
break;
fi
done < apps/file2
exit $RC
Thank you!
The ansewer to your question is using break 2:
while true; do
sleep 1
echo "outer loop"
while true; do
echo "inner loop"
break 2
done
done
I never use this, it is terrible when you want to understand or modify the code.
Already better is using a boolean
found_master=
while [ -n "${found_master}" ]; do
sleep 1
echo "outer loop"
while true; do
echo "inner loop"
found_master=true
break
done
done
When you do not need the variable found_master it is an ugly additional variable.
You can use a function
inner_loop() {
local i=0;
while ((i++ < 5)); do
((random=$RANDOM%5))
echo "Inner $i: ${random}"
if [ ${random} -eq 0 ]; then
echo "Returning 0"
return 0
fi
done;
return 1;
}
j=0
while ((j++ < 5 )); do
echo "Out loop $j"
inner_loop
if [ $? -eq 0 ]; then
echo "inner look broken"
break
fi
done
But your original problem can be handles without two while loops.
You can use grep -E "INFO|ERROR" file2 or combining the keywords. When the keywords are on different lines in file1, you can use grep -f file1 file2.
Replace condition with $(grep -c ${key} ${line}) -gt 0 like this:
echo "searching $key from the file $line
if [ $(grep -c ${key} ${line}) -eq 0 ]; then
It will count the each key-word in your log-file. If count=0 (pattern didn't found), running then. If found at least 1 key, running else, RC=1 and exit from loop.
And be sure, that your key-words can't be substrings of the longest words, or you will get an error.
Example:
[sahaquiel#sahaquiel-PC Stackoverflow]$ cat file
correctstringERROR and more useless text
ERROR thats really error string
[sahaquiel#sahaquiel-PC Stackoverflow]$ grep -c ERROR file
2
If you wish to avoid count 2 (because counting first string, obliviously, bad way), you should also add two keys for grep:
[sahaquiel#sahaquiel-PC Stackoverflow]$ grep -cow ERROR file
1
Now you have counted only the words equal to your key, not substrings in any useful strings.
I wrote the following code :
echo "Choose between the following options:"
echo "1 - Create a new file"
echo "2 - Write in an existing file"
echo "3 - Change the path of a file"
echo "4 - Display a file"
echo "5 - Exit"
read number
while [ $number -ne 1 -o $number -ne 2 -o $number -ne 3 -o $number -ne 4 -o $number -ne 5 ]
do
echo "Enter a number between 1 and 5"
read number
done
if [ $number -eq 1 ]; then
echo "Enter a folder name"
read name
while [ -e $name ]
do
echo "The file name already exists enter a new name:"
read name
done
touch $name
fi
if [ $number -eq 2 ]; then
echo "Enter the folder name you want to edit :"
read name
while [ ! -f $name ]
do
echo "The file you are looking does not exist. Enter another file name :"
read name
done
echo "Enter what you want to put in the file :"
read input
echo $input >> $name
fi
if [ $number -eq 3 ]; then
echo "Enter the folder name :"
read name
while [ ! -f $name ]
do
echo "The file you are looking does not exist. Enter another file name :"
read name
done
if [ $number -eq 4 ]; then
echo "Enter the folder name you want to see :"
read name
while [ ! -f $name ]
do
echo "The file you are looking does not exist. Enter another file name :"
read name
done
cat $name
fi
if [ $number -eq 5 ]; then
exit 0
fi
The code works just fine, but on the first condition :
while [ $number -ne 1 -o $number -ne 2 -o $number -ne 3 -o $number -ne 4 -o $number -ne 5 ]
I would like it to work to whatever number or string I put.
For example, if I put hello the program will crash.
Can someone tell me what should my first condition be?
Thank you for your help. And forgive me if my question is not in the rules of the forum (I just subscribed).
Your script says:
while [ $number -ne 1 -o $number -ne 2 -o $number -ne 3 -o $number -ne 4 -o $number -ne 5 ]
So, if $number is 1, it's not equal to 2. And if it's 2, it's not equal to 1. This will always evaluate as true, so you'll never exit the loop.
A variety of options exist that will be compatible with a numeric value and still gracefully handle non-numeric input. The following uses a basic regular expression to determine whether input is a digit from 1 to 5:
while read number && ! expr "$number" : '[1-5]$' >/dev/null; do
echo "Try again" >&2
done
You would probably be better off, though, using a case statement:
while read number; do
case "$number" in
1) function_1 ;;
2) function_2 ;;
... etc
*) echo "Invalid input, please try again." >&2; continue ;;
esac
break
done
The case statement is a more graceful way of expressing what might otherwise be achieved using if..elif..elif..fi:
while read number && ! expr "$number" : '[1-5]$' >/dev/null; do
echo "Try again" >&2
done
if [ "$number" = 1 ]; then
: do_something
elif [ "$number" = 2 ]; then
: do_something
elif [ "$number" = 3 ]; then
: do_something
elif [ "$number" = 4 ]; then
: do_something
elif [ "$number" = 5 ]; then
: do_something
else
echo "What am I doing?" >&2
fi
While this construct technically works, it's inelegant and harder to read. Much better to put your functionality into functions which get called from a case statement.
Note that it's always a good idea to quote your variables within a script like this. Do you know what happens with unquoted variables? If not, then quote your variables. :)
I would strongly suggest structuring your code with a case statement:
case $number in
1) code_for_1;;
2) code_for_2;;
...
*) echo "Invalid number" >&2;;
esac
If you wish to repeat (I would actually recommend taking the number as a command line argument instead of reading from stdin and aborting on an error), you could simply do:
while read number; do
case $number in
...
*) echo 'Invalid number' >&2; continue;;
esac
break
done
And write code_for_{1,2,3,4,5} as functions to factor out the logic. For example:
create_file() {
echo "Enter a file name"
while read name; do
if test -e "$name"; then
echo "The file $name already exists enter a new name" >&2
continue
fi
touch "$name"
break
done
}
while read number; do
case $number in
1) create_file;;
...
*) echo 'Invalid number' >&2; continue;;
esac
break
done
I have a UNIX script written in korn shell. I need to make it so that this statement:
while true
do
echo "What is the last name of the person you would like to modify:"
read last_name
if line=$(grep -i "^${last_name}:" "$2")
then
IFS=: read c1 c2 c3 c4 rest <<< "$line"
echo -e "Last Name: $c1\nFirst Name: $c2\nState: $c4"
while true
do
echo "What would you like to change the state to?:"
read state
if [[ $state -eq [A-Z] ]];then
echo "State: $state"
echo "This is a valid input"
break
else
echo "Not a valid input:"
fi
done
else
echo "ERROR: $last_name is not in database"
echo "Would you like to search again (y/n):"
read delete_choice
case $delete_choice in [Nn]) break;; esac
fi
done
;;
Specifically, I am having trouble with this code:
if [[ $state -eq [A-Z] ]];then
The point of this program is to modify a record in a text file but will only take the input of state abbreviations such as (MI, WA, KS, ....).
Try something like:
if echo $state | egrep -q '^[A-Z]{2}$'
then
...
fi
^[A-Z]{2}$ means your state starts and ends with CAPS alphabets of length two.
I am having trouble with the code below:
IFS=: read c1 c2 c3 c4 rest <<< "$line"
Don't get me wrong this code works good but it doesn't seem to be used for ksh. I basically need to write the same code without the "<<<". There is not much info on the "<<<" online. If anybody has any ideas it would be much appreciated.
EDIT:
Ok code is as follows for the entire portion of programming:
m|M)
#Create Modify Message
clear
echo " Modify Record "
echo -en '\n'
echo -en '\n'
while true
do
echo "What is the last name of the person you would like to modify:"
read last_name
if line=$(grep -i "^${last_name}:" "$2")
then
oldIFS=$IFS
IFS=:
set -- $line
IFS=$oldIFS
c1=$1
c2=$2
c3=$3
c4=$4
shift; shift; shift; shift
rest="$*"
echo -e "Last Name: $1\nFirst Name: $2\nState: $4"
while true
do
echo "What would you like to change the state to?:"
read state
if echo $state | egrep -q '^[A-Z]{2}$'
then
echo "State: $state"
echo "This is a valid input"
break
else
echo "Not a valid input:"
fi
done
echo -e "Last Name: $c1\nFirst Name: $c2\nState: $state"
echo "State value changed"
break
else
echo "ERROR: $last_name is not in database"
echo "Would you like to search again (y/n):"
read modify_choice
case $modify_choice in [Nn]) break;; esac
fi
done
;;
Ok so everything works except for the
echo -e "Last Name: $c1\nFirst Name: $c2\nState: $state"
It will just show:
Last Name:
First Name:
State:
So I can see it is not adding it to my echo correctly.
FINAL EDIT
CODE:
#Case statement for modifying an entry
m|M)
#Create Modify Message
clear
echo " Modify Record "
echo -en '\n'
echo -en '\n'
while true
do
echo "What is the last name of the person you would like to modify:"
read last_name
if line=$(grep -i "^${last_name}:" "$2")
then
echo "$line" |
while IFS=: read c1 c2 c3 c4 rest; do
echo -e "Last Name: $c1\nFirst Name: $c2\nState: $c4"
last=$c1
first=$c2
done
while true
do
echo "What would you like to change the state to?:"
read state
if echo $state | egrep -q '^[A-Z]{2}$'
then
echo "State: $state"
echo "This is a valid input"
break
else
echo "Not a valid input:"
fi
done
echo -e "Last Name: $last\nFirst Name: $first\nState: $state"
echo "State value changed"
break
else
echo "ERROR: $last_name is not in database"
echo "Would you like to search again (y/n):"
read modify_choice
case $modify_choice in [Nn]) break;; esac
fi
done
;;
A here string in Bash
command <<<"string"
is basically equivalent to
echo "string" | command
with the obvious exception that the latter uses a pipeline, which means you cannot meaningfully use it with read in particular. A common workaround is to use the set builtin to capture tokens from a string or an external command:
oldIFS=$IFS
IFS=:
set -- $line # no quotes
IFS=$oldIFS
c1=$1
c2=$2
c3=$3
c4=$4
shift; shift; shift; shift
rest="$*" # loses spacing / quoting
Another workaround is to use a loop which iterates just once; this may seem elegant at first, but can lead to rather clunky code if the body of the pseudo-loop is long or complex.
echo "$line" |
while IFS=: read c1 c2 c3 c4 rest; do
: stuff which uses those variables
done
This works around the problem that echo stuff | read variable will run read in a child process and thus immediately forget the value of variable -- the body of the while loop is all the same process in which the read happened, and so the values of the variables it initialized are visible inside the loop.
Another, similar workaround is to delegate the reading and procesIng to a function;
process () {
IFS=: read c1 c2 c3 c4 rest
: stuff which uses those variables
}
echo "$line" | process
Whether this is clunky or elegant depends a lot on what happens in the function. If it's neatly encapsulated, it can be rather attractive; but if you end up passing in a bunch of unrelated variables (or worse, modifying globals inside the function!) it can be quite the opposite.
Script performs two things
1.Enables the user input a file name
2.Enables the user to input a line number to view the content
echo "Enter the file name"
read fname
find / -name "$fname" > /tmp/newone.txt
if test $? -eq 0
then
{
echo "File found"
echo "The no of line in the file $fname is `cat /tmp/newone.txt | wc|awk '{pri
nt $1}'`"
echo "Enter the line no"
read lcnt
sed '"$lcnt" p' "$fname"
}
else
{
echo "File not found"
}
fi
Issue
1.Getting error in the sed part
Error message "sed: -e expression #1, char 3: extra characters after command"
how to rectify it ?
2.Can i redirect the output of 'find' to a variable
For example
$flloc =/tmp/newone.txt
so i will be able to use '$flloc' instead of the absolute path
1) This is how you'd go about using your variable in the sed command:
echo "Line no: "
read lcnt
sed -n "$lcnt p" $fname
What was wrong with your original expression is that bash variables aren't interpreted when you use single quotes. Example:
lcnt=5
# prints $lcnt
echo '$lcnt'
# prints 5
echo "$lcnt"
2) To store your find output to a variable, simply do this:
floc=`find / -name $fname` # Here I'm using backticks, not single quotes.