sed erasing everything instead of changing single field - shell

hi i have this code that is meant to update a single field title but it ended with it erasing everything. need some help please
BookDB.txt:
The Hunger Games:Suzanne Collins:1:1:1
Weapon X:Stan Lee:1:1:1
Weapon X:stan lim:1:1:1
function update_title
{
echo "Title: "
read title
echo "Author: "
read author
grep -iqs "$title:$author:" BookDB.txt && echo "Book Found!"
echo "New Title: "
read title_new
sed -i "/^$author:/ s/$title/$title_new/" BookDB.txt || tee BookDB.txt && echo "Book Title has been updated sucessfully!"
}

Answer for Revised Question
The sed command needs to be changed because the title comes first on the line, not the author. Also the tee command removed for the same reasons as explained below. Thus, define the function as follows:
update_title ()
{
echo "Title: ";
read title;
echo "Author: ";
read author;
grep -iqs "$title:$author:" BookDB.txt && echo "Book Found!";
echo "New Title: ";
read title_new;
sed -i "/:$author:/ s/^$title/$title_new/" BookDB.txt && echo "Book Title has been updated sucessfully!"
}
As an example, invoke the function:
$ update_title
Title:
Weapon X
Author:
Stan Lee
Book Found!
New Title:
Weapon XYZ
Book Title has been updated sucessfully!
The result is:
$ cat BookDB.txt
The Hunger Games:Suzanne Collins:1:1:1
Weapon XYZ:Stan Lee:1:1:1
Weapon X:stan lim:1:1:1
Answer for Original Question
Replace:
sed -i "/^$author:/ s/$title/$title_new/" BookDB.txt || tee BookDB.txt && echo "Book Title has been updated sucessfully!"
With this:
sed -i "/^$author:/ s/$title/$title_new/" BookDB.txt && echo "Book Title has been updated sucessfully!"
The first line asks sed to rewrite BookDB.txt in place. It then asks tee to overwrite the same file. The solution is to do the first only.
Explanation
Commands such as:
sed 's/old/new/' file >file
or:
sed 's/old/new/' file | tee file
will be unreliable. They attempt to read from a file while writing to it at the same time. Depending on how files are buffered, this type of command may occasionally work but are never to be trusted.
sed -i is specifically designed to avoid this issue. It writes to a temporary file and, after all changes have been successfully save, then overwrites the source file.
Example
Let's define this function:
function update_title
{
echo "Title: "
read title
echo "Author: "
read author
grep -iqs "$title:$author:" BookDB.txt && echo "Book Found!"
echo "New Title: "
read title_new
sed -i "/^$author:/ s/$title/$title_new/" BookDB.txt && echo "Book Title has been updated sucessfully!"
}
Now, let's use it:
$ cat BookDB.txt
Tim:Old Title:
Tom:Older Title:
$ update_title
Title:
Old Title
Author:
Tim
New Title:
New Title
Book Title has been updated sucessfully!
$ cat BookDB.txt
Tim:New Title:
Tom:Older Title:
The old title was successfully replaced.
Warning
This function substitutes shell variables directly into sed commands. If the user is unaware of the power of sed-active characters, the results may be surprising.
As an example:
$ update_title
Title:
.*
Author:
Tim
New Title:
This & That
Book Title has been updated sucessfully!
Now, observe the new file:
$ cat BookDB.txt
This Tim:New Title: That
Tom:Older Title:
OSX
On OSX, the -i option to sed requires an argument. For OSX, and possibly other BSD platforms, replace the sed line in the function definition with:
sed -i .bak "/^$author:/ s/$title/$title_new/" BookDB.txt && echo "Book Title has been updated sucessfully!"
Or,
sed -i "" "/^$author:/ s/$title/$title_new/" BookDB.txt && echo "Book Title has been updated sucessfully!"

use this , because first field is definitely the title so you have to put title infront
sed -i "/^$title:$author:/ s/$title/$title_new/" BookDB.txt

Related

Trying to edit information in File using sed

i have this code below to update information in a file but i only wish for 1 of the many duplicates to be edited. i only wish for Weapon X's field Stan Lee to be edited but when i ran the code it edited all of the Different field's Stan Lee.
e.g
$ cat BookDB.txt
Weapon X:Stan Lee
Gambit:Stan Lee
Avengers:Stan Lee
Code:
function update_author
{
echo "Title: "
read title_upa
echo "Author: "
read author_upa
grep -iqs "$title_upa:$author_upa:" BookDB.txt && echo "Book Found"
echo "New Author: "
read author_upar
sed -i s/"$author_upa"/"$author_upar"/ BookDB.txt || tee BookDB.txt && echo "Book Author has been updated sucessfully!"
}
I saw a similar question to this so here is my solution , change the sed line by getting title and author together before switching the author
sed -i "/^$title:$author:/ s/$author/$author_new/" BookDB.txt
sed -i "/^$title_upa:/ s/$author_upa/$author_upar/" BookDB.txt
The leading regex limits the replacement.
If the user types a value with a / character, this will break, so you should escape them:
sed -i "/^${title_upa//\//\\/}:/ s/${author_upa//\//\\/}/${author_upar//\//\\/}/" BookDB.txt
That's looking a little ridiculous. Let's try a different tool:
tmp=$(mktemp)
awk -F: -v OFS=: -v title="$title_upa" -v author="$author_upa" -v new="$author_upar" '
$1 == title && $2 == author {$2 = new}
{print}
' BookDB.txt > "$tmp" && mv "$tmp" BookDB.txt

linux script to update price

i would like to do a code that prompts user to input the title and author of a book and i want to use grep to get the data based on just the title and author and echo it back for the user to see and edit the price without having the user to enter the old price
I need help in trying to get the $price Variable just by entering the title and author
function update_cost
{
echo "Title: "
read title
echo "Author: "
read author
grep -iqs "$title:$author:$price:" BookDB.txt && echo "$title:$author:$price:"
echo "New Price: "
read price_r
sed -i "/^$title:$author:/ s/$price/$price_r" BookDB.txt || tee BookDB.txt && echo "Book Price has been updated sucessfully!"
}
In regards to the question above i came up with an answer for that. Hope it Helps
price=$(echo "$result" | cut -f 3 -d ":")
I manage to get the 3rd field of what the user input by matching it with the result line followed by using the sed line i edited the third field.
function update_cost
{
echo "Enter Title: "
read title
echo "Enter Author: "
read author
result=$(grep -ise "$title\:$author" BookDB.txt)
price=$(echo "$result" | cut -f 3 -d ":")
grep -iqs "$title:$author:$price:" BookDB.txt && echo "Book Found!"
echo "New Price: "
read new_price
sed -i "/^$title:$author:$price:/ s/$price/$new_price/" BookDB.txt || tee BookDB.txt && echo "Price has been updated sucessfully!"
}
awk is better suited to extracting a field from the file and assigning it to a variable.
function update_cost
{
echo "Title: "
read title
echo "Author: "
read author
price=$(awk -F: -v title="$title" -v author="$author" '$1 == title && $2 == author { print $3 }' BookDB.txt)
echo "Old price is $price"
echo "New Price: "
read price_r
sed -i "/^$title:$author:/ s/:$price:/:$price_r:/" BookDB.txt || tee BookDB.txt && echo "Book Price has been updated sucessfully!"
}

Bash grep from text file and edit

I am doing a update on the file test.txt which stores some info about storybooks
I've the code below for the function but i realise that using this method of grep, i am unable to get the rest of the values for price quantity and sold which are not mentioned in the grep command.
Is there a better command i can use for this case?
Thanks in advance for replies. :)
echo "Title: "
read title
echo ""
echo "Author: "
read author
echo ""
echo "Number of copies sold: "
read numSold
if grep -iq "$title:$author:" test.txt
then
echo "Current Book Info :"
echo "$title, $author, $price, $quantity, $sold"
echo ""
newQuantity=`expr $quantity - $numSold`
newSold=`expr $sold + $numSold`
sed -i "s/^\($title:$author:[^:]*\):[^:]*:/\1:$newQuantity:/" BookDB.txt
sed -i "s/^\($title:$author:.*\):.*$/\1:$newSold/" BookDB.txt
echo "New Book Info :"
echo "$title, $author, $price, $quantity, $sold"
fi
Well unfortunately the build of the test.txt is unknow.
If you could show an example that can help.
But if the Price quantity and other staff are in the 1-2 line away from the line you grepping you can use the grep -A 2 (which will grep 2 next lines) or -B 2 (2 lines from above)
after that u can just remove the '\n'from the results and print it :
grep -A 2 "$title:$author:*" | tr '\n' ' '
this will give you a line (build out of 3 lines) with all the data.
Of course its based on the how test.txt is build.
some thing like this :
> grep -A 2 "Konan Doil:Story" test.txt
Konan Doil:Story
Price - $$$$$
Quantity - XXXX, Sold - 99999
In response to the comment below :
I think its better then use array to save the input. after that u can do
with array what ever you want as the values are same
(Index 0 = Author, 1 = Book Name ... etc.)
>IFS=$':'; i=0 ; for line in $(grep "Konan Doil:Good" test.txt) ; do arr[$i]=$line; i=$(($i + 1)) ; done
>echo "${arr[0]}"
Konan Doil
>echo "${arr[1]}"
Good story
>echo "${arr[3]}"
XXXXX
>echo "${arr[4]}"
YYYYYY
>echo "${arr[2]}"
$$$$$
>

I am asking for the user to input a title, but I want to compare the "input" with my text file

#!/bin/bash
library=mylibrary
tmp=mylibrary.tmp
function add_book {
echo Please enter the book title:
read title
existed=`awk -F '$1 == $title' $library | wc -l`
echo $existed
if (( $existed == 0 ))
then
echo Please enter the author:
read author
location=library
updated=$title,$author,$location,`date +%F`
echo $updated >> $library
echo $updated return successfully!
else
echo "Book Exist!"
fi
((WHAT I WANT TO DO IS COMPARE THE TITLES AND IF THE TITLE EXIST THEN ECHO "BOOK EXIST"
I WANT IT TO COMPARE THE INPUT WITH THE TEXT FILE))
HERE IS A SAMPLE TEXT FILE:
Title,Author,Location,Date Added
jj,jj,library,2013-11-14
hjj,hj,library,2013-11-14
jhj,hjh,library,2013-11-14
Your script has quite a few issues but this seems to be a blocker issue:
existed=`awk -F '$1 == $title' $library | wc -l`
Change that to:
existed=$(awk -v title="$title" -F '$1 == title{i++} End{print i}' "$library")
SHELL doesn't expand varialle inside single quotes
To pass a shell variable to awk use -v name="$value" syntax

How to use sed to find the corresponding place in a text file and update it shell

Book name:author:price:Qty:Qty Sold
==================================
harry potter:james:12.99:197:101
===============================
I want to update the QTY which is the value of 197 in this case but i cant seems to be able to update the value with my program please help me as i have just started learning shell programming. Thanks
function update_qty_available
{
grep -c "$title:$author" BookDB.txt > /dev/null # Look for a line with matching values
if [ $? == 0 ];
then # If found then offer to change Qty
echo "Update Qty to what?"
read newQty
sed -i "s/\($title:$author\):[^:]:[^:]*:/\1:$newQty/" BookDB.txt
echo "Book's Qty has been updated successfully!"
fi
You're missing an asterisk after the first character class. And don't forget to expand the group.
Refactored to use grep -q; fix the if idiom; anchor the search to start of line; use read -p instead of a separate echo; capture the price inside the parens so that it's not lost; add the missing repeat in the regex; and add back the separator colon after the new qty value; also, add an else clause so that the function doesn't fail silently.
function update_qty_available
{
if grep -q "^$title:$author:" BookDB.txt
then
read -p "Update Qty to what?" newQty
sed -i "s/^\($title:$author:[^:]*\):[^:]*:/\1:$newQty:/" BookDB.txt
echo "Book's Qty has been updated successfully!"
else
echo "$0: BookDB.Txt: no '$title' by '$author'" >&2
fi
}
Here is how you'd do with awk:
Content of script.awk:
BEGIN {
FS=OFS=":"
printf "Enter the book's name: "
getline name < "-"
printf "Enter author's name: "
getline author < "-"
printf "Enter new quantity: "
getline newQty < "-"
}
$1==name && $2==author { $4=newQty }1
Run:
$ cat BookDB.txt
Book name:author:price:Qty:Qty Sold
==================================
harry potter:james:12.99:197:101
===============================
$ awk -f script.awk BookDB.txt
Enter the book's name: harry potter
Enter author's name: james
Enter new quantity: 5000
Book name:author:price:Qty:Qty Sold
==================================
harry potter:james:12.99:5000:101
===============================

Resources