Trying to edit information in File using sed - shell

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

Related

How can I remove a line that contains a variable in bash?

I know some people may say the solution is with sed but it didn't work for me
so, the thing is that I read a var with read var then I want to know how to control if that var exists in a column specified by me of my archive, and if it doesnt just keep asking Please enter a valid code, and if its correct just delete that line. thanks
CODE
read var
sed -i '/$var/d' file.txt
And i want to put some short of tester that confirm if u put a valid code or not.
The structure of the file is
code;Name;Surname
There's no spaces or odd bits to parse, so sed needs no single quotes here:
read var
sed -i /"$var"/d file.txt
And a demo -- make a list from 1 to 3, remove 2:
seq 3 > three.txt; var=2; sed -i /"$var"/d three.txt ; cat three.txt
Outputs:
1
3
The following used awk to search and remove lines which first column is $code. If a line is removed then awk will exit successfully and break will be called.
file="input_file"
while :; do
echo "Enter valid code:"
read -r code
[ -n "$code" ] || continue
awk -F';' -v c="$code" '$1 == c {f=1;next}1;END{exit(f?0:1)}' \
"$file" > "$file.out" && break
done
mv "$file.out" "$file"
This will continue to ask for a code in $file until the user enters a valid code at which point $file.out is created and the iteration broken.
Then file.out is renamed to file. Technically $file.out is created each time.
You can have $var or ${var}expanded with
read var
sed -i '/'${var}'/d' file.txt
But what will happen when $var has a space? Nothing good, so use double quotes as well:
read var
sed -i '/'"${var}"'/d' file.txt
I was trying with awk in below file :
$cat test.txt
code;Name;Surname
xyz;n1;s1
abc;dd;ff
xyz;w;t
abc;ft;op
It will print the lines that is going to delete .But I am not able to figure out how to delete the line from awk after printing the info .
$var=xyz | awk -v var="$var" -F ";" '{ if ($1 == var ) print "FOUND " var " And Going to delete the line" NR " " $0 ; }' test.txt
FOUND xyz And Going to delete the line2 xyz;n1;s1
FOUND xyz And Going to delete the line4 xyz;w;t
Below command will display the info and will delete the date . But it will take 2 parsing of the input file .
$var=xyz | awk -v var="$var" -F ";" '{ if ($1 == var ) print "FOUND " var " And Going to delete the line" NR " " $0 ; }' test.txt && awk -v var="$var" -F ";" '($1 != var)' test.txt > _tmp_ && mv _tmp_ test.txt
FOUND xyz And Going to delete the line2 xyz;n1;s1
FOUND xyz And Going to delete the line4 xyz;w;t
New File after deletion :
$cat test.txt
code;Name;Surname
abc;dd;ff
abc;ft;op

sed erasing everything instead of changing single field

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

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!"
}

Search for a part of a string in a file in shell

I have a file with a list of names in it. I am trying to search for names with a certain group of strings and print them to a new file in shell. Can you please help?
Here's what I am looking at.
Text file(names.txt) contains names like
New York_USA
Delhi
Moscow
Tokyo
Austin_USA
Beijing
Chicago_USA
I am trying to get the names with _USA in a seperate file.
Here's what I have tried
#/bin/ksh
for city in "cat names.txt"
do
if ["$city" =~ "*_USA"]
then
echo "$city in USA" > USAnames.txt
fi
done
You just need to execute the cat command instead of quoting it, fix your test operator ([ --> [[), and append >> instead of overwrite >, and write a regex instead of a glob pattern:
#/bin/ksh
for city in `cat names.txt` # notice the back quotes
do
if [[ "$city" =~ .*_USA ]]
then
echo "$city in USA" >> USAnames.txt
fi
done
A more idiomatic way would perhaps be:
#/bin/ksh
while read city
do
if [[ "$city" =~ .*_USA ]]
then
echo "$city in USA" >> USAnames.txt
fi
done < names.txt
Simply use grep :
grep '_USA$' your_file> new_file
Use awk or sed will be easier
if you want the output to be like this
New York_USA in USA
Austin_USA in USA
Chicago_USA in USA
use
awk '/_USA$/ {print $0, "in USA"}' names.txt > USAnames.txt
or sed
sed -n 's/\(.*\)_USA$/\0 in USA/p' names.txt > USAnames.txt
or if you want output to be
New York in USA
Austin in USA
Chicago in USA
use
awk -F_ '/_USA$/ {print $1,"in USA"}' names.txt > USAnames.txt
or sed
sed -n 's/\(.*\)_USA$/\1 in USA/p' names.txt > USAnames.txt
Edit
you can also use perl to print out the USA cities
#!/usr/bin/env perl
open(NAMES, "names.txt");
while (<NAMES>) {
chomp;
print "$_ in USA\n" if /_USA$/; # Chicago_USA in USA
# print "$1 in USA\n" if /(.*)_USA$/; # Chicago in USA
}
close(NAMES);

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