Grep statement in If-Then-Else Statment is not giving intended results - unix

The intended result of this program is to perform the deletion of a record from a temporary file by searching for the last name. If the name is in the file it will display a message that the record is deleted from the file. The message will have a last and first name of the person deleted. If there is no record for the name entered, display an error message indicating that the name is not in the file. The error message will have the last name of the person searched.
For the most part I have figured this code completely out. However, I am running into errors that are giving me a lot of trouble.
Code is as follows:
#While loop
delete_choice="y"
until [[ $delete_choice == "n" ]]
do
#Create Delete Message
clear
echo " Delete Record "
echo -en '\n'
echo -en '\n'
echo "What is the last name of the person you want to delete:"
read last_name
if line=$( grep -Fi "$last_name" "$2")
then
IFS=: read c1 c2 rest <<< "$line"
echo -e "Last Name: $c1\nFirst Name: $c2\nSTUDENT RECORD HAS BEEN DELETED FROM FILE"
sed "/$line/d" $2
else
echo "ERROR: $last_name is not in database"
echo "Would you like to search again (y/n)"
read delete_choice
fi
done
;;
So what happens when I execute this code is it brings up the delete message and asks me to input a last name. I put in a last name "smith", when I do this it skips the whole if statement and goes right to the end of the loop and then brings me right up to asking me what the last name is of the person I want to delete. So obviously it is concentrated in the grep statement somewhere. Another odd thing is if I put a name that I know is not in there it will take me to the else statement and give me the error message and ask me if I want to search again.
Any help would be appreciated, I been searching for hours with the grep statement and cannot figure it out.
Additionally: On a side note does anybody know how to make it so I can input "n" or "N" in the Until...Do statement to keep the loop going?
EDIT:
Ok I fixed all the other problems in this code but there is just one problem I cannot fix. Every time I delete an entry from the file it doesn't allow me to execute the echo command.
Code is as follows:
d|D)
#While loop
delete_choice="y"
while true
do
#Create Delete Message
clear
echo " Delete Record "
echo -en '\n'
echo -en '\n'
echo "What is the last name of the person you want to delete:"
read last_name
if line=$(grep -i "^${last_name}:" "$2")
then
echo "$line" |
while IFS=: read c1 c2 rest; do
last=$c1
first=$c2
sed -i "/^$c1:$c2:/d" "$2"
done
echo -e "Last Name: $last\nFirst Name: $first\nSTUDENT RECORD HAS BEEN DELETED FROM FILE"
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
;;
As you can see I execute the echo command but it never displays. However, I can tell that it deletes the entry from the file when I exit the program and check it. Does anybody know how to make it display the echo command correctly?

You have used <<< which is a Bash-only feature.
The proper way to debug your problem is to run your script with ksh -x script arg (or possibly sh -x script arg if your script was a pure Bourne shell script; but this one isn't).
However, you have some peculiarities in your code. Allow me to offer a refactoring.
delete_choice="y"
until [[ "$delete_choice" == "n" ]]
do # Indent your control structures
clear
echo " Delete Record"
echo # Massive trailing whitespace removed ----------^^^
echo # Just echo to output a new line
echo "What is the last name of the person you want to delete:"
read last_name
case $last_name in [Nn]) break;; esac # Exit on "n"
if line=$(grep -Fi "$last_name" "$2")
then # Indent your control structures
file=$2
oldIFS=$IFS
IFS=:
set -- "$line" # break input into $1, $2, etc
IFS=$oldIFS
echo -e "Last Name: $1\nFirst Name: $2\nI SCREAM IN UPPER CASE"
sed -i "/^$1:$2:/d" "$file" # note quoting and -i and changed variable
else
echo "ERROR: $last_name is not in database"
echo "Would you like to search again (y/n)"
read delete_choice
fi
done
Another problem here was unrelated to your question. Directly executing the user's input in the regex would be extremely careless -- what if somebody input just a for a last name? The changed code will only delete one user, regardless of how short the input; but perhaps the grep should also be anchored to use a tighter search expression.
There is still a possibility that a peculiar name in the database would not match itself, or match more than itself. For example, I use my second given name, so where my full name is required, I often input my name as "First *Middle Last" with an asterisk next to the name I use, which is the convention where I live; but the string *Middle will not match itself in a regular expression.
Also, without -i, the sed script would simply print a copy of the database to standard output.
The delete_choice variable is not getting updated anywhere within your code so you could change the exterior loop to just while true and handle exiting by breaking out of the loop, like I have done. I didn't change the until on the vague speculation that this might be used in other parts of your code which you aren't showing.

Related

I want to append to echo statement in one line in UNIX

read Name?" Enter Full name "
First= echo $Name | cut -d ' ' -f1
I am using this command to cut Full name, but it automatically print first name. which I don't want.
I want an output like
James, This is prime number.
In the shell I use (bash on MacOS), this following works.
read -p "Enter full name " first last
echo Hi $first, you are the man
Please specify what shell you are using.
Another thing to note, is that in order to assign to the variable 'First' in your question, in bash you need to do surround the command with backticks `` or the $() construction, as follows.
First=$(echo $Name | cut -d ' ' -f1)

Expressing tail through a variable

So I have a chunk of formatted text, I basically need to use awk to get certain columns out of it. The first thing I did was get rid of the first 10 lines (the header information, irrelevant to the info I need).
Next I got the tail by taking the total lines in the file minus 10.
Here's some code:
import=$HOME/$1
if [ -f "$import" ]
then
#file exists
echo "File Exists."
totalLines=`wc -l < $import`
linesMinus=`expr $totalLines - 10`
tail -n $linesMinus $import
headless=`tail -n $linesMinus $import`
else
#file does not exist
echo "File does not exist."
fi
Now I need to save this tail into a variable (or maybe even separate file) so I can access the columns.
The problem comes here:
headless=`tail -n $linesMinus $import`
When I save the code into this variable and then try to echo it back out, it's all unformatted and I can't distinguish columns to use awk on.
How can I save the tail of this file without compromising the formatting?
Just use Awk. It can do everything you need all at once and all in one program.
E.g. to skip the first 10 lines, then print the second, third, and fourth columns separated by spaces for all remaining lines from INPUT_FILE:
awk 'NR <= 10 {next;}
{print $2 " " $3 " " $4;}' INPUT_FILE
Figured it out, I kind of answered my own question when I asked it. All I did was redirect the tail command to a file in the home directory and I can cat that file. Gotta remember to delete it at the end though!

How to create options in KSH script

I am creating a KSH interface script that will call other scripts based on the users input. The other scripts are Encrypt and Decrypt. Each one of these scripts receive parameters. I have seen someone execute a script before using "-" + first letter of a script name before. How do I do this for my script? So for example if my script is called menu and the user typed in : menu -e *UserID Filename.txt* the script would run and the encrypt script would be executed along with the associated parameters. So far my script takes in the encrypt/decrypt script option as a parameter. Here is my script:
#!/bin/ksh
#I want this parameter to become an
action=$1
if [ $1 = "" ]
then
print_message "Parameters not satisfied"
exit 1
fi
#check for action commands
if [ $1 = "encrypt" ]
then
dest=$2
fileName=$3
./Escript $dest $fileName
elif [ $1 = "decrypt" ]
then
outputF=$2
encryptedF=$3
./Dscript $outputF $encryptedF
else
print "Parameters not satisfied. Please enter encrypt or decrypt plus-n arguments"
fi
Thanks for the help!
There isn't any kind of automatic way to turn a parameter into another script to run; what you're doing is pretty much how you would do it. Check the parameter, and based on the contents, run the two different scripts.
You can structure it somewhat more nicely using case, and you can pass the later parameters directly through to the other script using "$#", with a shift to strip off the first parameter. Something like:
[ $# -ge 1 ] || (echo "Not enough parameters"; exit 1)
command=$1
shift
case $command in
-e|--encrypt) ./escript "$#" ;;
-d|--decrypt) ./dscript "$#" ;;
*) echo "Unknown option $command"; exit 1 ;;
esac
This also demonstrates how you can implement both short and long options, by providing two different strings to match against in a single case statement (-e and --encrypt), in case that's what you were asking about. You can also use globs, like -e*) to allow any option starting with -e such as -e, -encrypt, -elephant, though this may not be what you're looking for.

How to read 1 symbol in zsh?

I need to get exactly one character from console and not print it.
I've tried to use read -en 1 as I did using bash. But this doesn't work at all.
And vared doesn't seem to have such option.
How to read 1 symbol in zsh? (I'm using zsh v.4.3.11 and v.5.0.2)
read -sk
From the documentation:
-s
Don’t echo back characters if reading from the terminal. Currently does not work with the -q option.
-k [ num ]
Read only one (or num) characters. All are assigned to the first name, without word splitting. This flag is ignored when -q is present. Input is read from the terminal unless one of -u or -p is present. This option may also be used within zle widgets.
Note that despite the mnemonic ‘key’ this option does read full characters, which may consist of multiple bytes if the option MULTIBYTE is set.
If you want your script to be a bit more portable you can do something like this:
y=$(bash -c "read -n 1 c; echo \$c")
read reads from the terminal by default:
% date | read -sk1 "?Enter one char: "; echo $REPLY
Enter one char: X
Note above:
The output of date is discarded
The X is printed by the echo, not when the user enters it.
To read from a pipeline, use file descriptor 0:
% echo foobar | read -rk1 -u0; echo $REPLY
f
% echo $ZSH_VERSION
5.5.1
Try something like
read line
c=`echo $line | cut -c1`
echo $c

Why can't I read user input properly inside a UNIX while loop?

I'm using the bourne shell in UNIX, and am running into the following problem:
#!/bin/sh
while read line
do
echo $line
if [ $x = "true" ]
then
echo "something"
read choice
echo $choice
else
echo "something"
fi
done <file.txt
The problem I have here is that UNIX will not wait for user input in the read command - it just plows on through instead of waiting for what the user types in. How can I make unix wait for user input?
It is because you are asking the program to read from the file file.txt:
done <file.txt
Also looks like you have a typo here:
if [ $x = "true" ]
^^
which should be "$line". Also note the ", without them your program will break if the word read from the file has a space in it.
The redirection of standard input by the <file.txt at the end of while ... done <file.txt affects the whole while loop, including the read choice as well as the read line. It's not just failing to stop - it's consuming a line of your input file too.
Here's one way to solve the problem...
You can save the original standard input by using the somewhat obscure (even by shell standards):
exec 3<&0
which opens file descriptor 3 to refer to the original file descriptor 0, which is the original standard input. (File descriptors 0, 1 and 2 are standard input, output and error respectively.) And then you can redirect just the input of read choice to come from file descriptor 3 by doing read choice <&3.
Complete working script (I wasn't sure where x was supposed to come from, so I just bodged it to make it work):
#!/bin/sh
x=true # to make the example work
exec 3<&0
while read line
do
echo $line
if [ $x = "true" ]
then
echo "something"
read choice <&3
else
echo "something"
fi
done <file.txt
I don't do much shell scripting, but i think 'read choice' should be 'read $choice'

Resources