Bash grep from text file and edit - bash

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]}"
$$$$$
>

Related

Use awk to print largest alphanumeric grep result and create variable next in sequence

I have a source file with the following information in it.
WABEL8499IPM101
WABEL8499IPM102
WABEL8499IPM103
WABEL8499IPM104
WABEL8499IPM105
WABEL8499IPM106
WABEL8499IPM107
WABEL8499IPM108
I need to be able to find the largest name in the sequence and then create a new variable with the next logical name in the sequence. I need to be able to create multiple if necessary. For example:
Use grep to search the file for WABEL8499IPM which shows all of the above results. I need to find WABEL8499IPM108 because it's the largest in the sequence and then create a new variable (how many depends on what the user inputs) with the value WABEL8499IPM109. If user inputs a quantity of 2 then I need both 109 and 110. My goal is to build a bash script to input the base name (without the last 3 digits), find the largest in the sequence and then output to a log file the next names in the sequence however many times the user needs.
I'm not really sure where to start. I can find all using grep but having difficulty finding only the largest value/sequence. The user will only input the base name because they won't know the last 3 digits. Currently I don't have any code that works.
SRCFILE="~/Desktop/deviceinfo.csv"
LOGDIR="~/Desktop/"
LOGFILE="$LOGDIR/DeviceNames.csv"
echo -e "\n"
echo "What is the base device name?"
read deviceName
echo "How many device names do you need?"
read quantityName
lines=$(grep -c "$deviceName" $SRCFILE)
echo -e "\n"
echo "There are $lines results."
deviceResults=$(grep -F "$deviceName" $SRCFILE)
echo -e "\n"
echo Device Name\'s Currently Enrolled:
echo "$deviceResults"
echo -e "\n"
echo "Your output file has been created."
CODE FOR CREATING OUTPUT FILE HERE
echo "$deviceName1" >> "$LOGFILE"
echo "$deviceName2" >> "$LOGFILE"
echo "$deviceName3" >> "$LOGFILE"
Would there be a way with this method to use a reference file for the input? For example if I had to research and create multiple names with different quantities could we use an input reference file for that so we don't have to type them each individually and run the script multiple times?
SRCFILE="~/Desktop/deviceinfo.csv"
LOGDIR="~/Desktop/"
LOGFILE="$LOGDIR/DeviceNames.csv"
# base name, such as "WABEL8499IPM"
device_name=$1
# quantity, such as "2"
quantityNum=$2
# the largest in sequence, such as "WABEL8499IPM108"
max_sequence_name=$(cat $SRCFILE | grep -o -e "$device_name[0-9]*" | sort --reverse | head -n 1)
# extract the last 3digit number (such as "108") from max_sequence_name
max_sequence_num=$(echo $max_sequence_name | rev | cut -c 1-3 | rev)
# creat a sequence of files starting from "WABEL8499IPM101" if there is not any "WABEL8499IPM".
if [ -z "$max_sequence_name" ];
then
max_sequence_name=device_name
max_sequence_num=100
fi
# create new sequence_name
# such as ["WABEL8499IPM109", "WABEL8499IPM110"]
array_new_sequence_name=()
for i in $(seq 1 $quantityNum);
do
cnum=$((max_sequence_num + i))
array_new_sequence_name+=($(echo $device_name$cnum))
done
#CODE FOR CREATING OUTPUT FILE HERE
#for fn in ${array_new_sequence_name[#]}; do touch $fn; done;
# write log
for sqn in ${array_new_sequence_name[#]};
do
echo $sqn >> $LOGFILE
done
Usage:
bash test.sh WABEL8499IPM 2
Result in the log file:
WABEL8499IPM109
WABEL8499IPM110
EDITED
The input reference file (input.txt) :
WABEL8499IPM,2
WABEL8555IPM,6
WABEL8444IPM,5
The driver shell script :
INPFIL="./input.txt"
PSRC="./test.sh"
cat $INPFIL | while read line;
do
device_name=`echo $line | cut -d "," -f 1`
quantity_num=`echo $line | cut -d "," -f 2`
bash $PSRC $device_name $quantity_num
done;
You can try
logdir="~/Desktop/"
srcfile="$logdir/deviceinfo.csv"
logfile="$logdir/DeviceNames.csv"
echo
read -p "What is the base device name? " deviceName
echo
read -p "How many device names do you need? " quantityName
echo
awk -v name="$deviceName" \
-v q="$quantityName" \
-v lelog="$logfile" '
$0 ~ "^"name {
sub(name,"")
a=a>$0?a:$0
}
END {
if ( a )
for ( i = 1 ; i <= q ; i++ )
print name ( a + i ) >> lelog
}
' "$srcfile"

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

Count mutiple occurences of a word on the same line using grep

Here I made a small script that take input from user searching some pattern from a file and displays required no of lines from that file where the pattern is found. Although this code is searching the pattern line wise due to standard grep practice. I mean if the pattern occurs twice on the same line, i want the output to print twice. Hope I make some sense.
#!/bin/sh
cat /dev/null>copy.txt
echo "Please enter the sentence you want to search:"
read "inputVar"
echo "Please enter the name of the file in which you want to search:"
read "inputFileName"
echo "Please enter the number of lines you want to copy:"
read "inputLineNumber"
[[-z "$inputLineNumber"]] || inputLineNumber=20
cat /dev/null > copy.txt
for N in `grep -n $inputVar $inputFileName | cut -d ":" -f1`
do
LIMIT=`expr $N + $inputLineNumber`
sed -n $N,${LIMIT}p $inputFileName >> copy.txt
echo "-----------------------" >> copy.txt
done
cat copy.txt
As I understood, the task is to count number of pattern occurrences in line. It can be done like so:
count=$((`echo "$line" | sed -e "s|$pattern|\n|g" | wc -l` - 1))
Suppose you have one file to read. Then, code will be following:
#!/bin/bash
file=$1
pattern="an."
#reading file line by line
cat -n $file | while read input
do
#storing line to $tmp
tmp=`echo $input | grep "$pattern"`
#counting occurrences count
count=$((`echo "$tmp" | sed -e "s|$pattern|\n|g" | wc -l` - 1))
#printing $tmp line $count times
for i in `seq 1 $count`
do
echo $tmp
done
done
I checked this for pattern "an." and input:
I pass here an example of many 'an' letters
an
ananas
an-an-as
Output is:
$ ./test.sh input
1 I pass here an example of many 'an' letters
1 I pass here an example of many 'an' letters
1 I pass here an example of many 'an' letters
3 ananas
4 an-an-as
4 an-an-as
Adapt this to your needs.
How about using awk?
Assume the pattern you are searching for is in variable $pattern and the file you are checking is $file
The
count=`awk 'BEGIN{n=0}{n+=split($0,a,"'$pattern'")-1}END {print n}' $file`
or for a line
count=`echo $line | awk '{n=split($0,a,"'$pattern'")-1;print n}`

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
===============================

Grepping correct value in 'else'

The following is a snippet from a daily report script that checks on the SMART status of disks.
I'd like the full value of $STATUS to be printed in the 'else' clause, however, but it's truncating it now to only the first word ($3).
How can I overcome this?
echo "SMART STATUS" >> $LOGS
echo "--------------------------" >> $LOGS
DISKS=( 0 1 2 3 4 5 6 7 )
for i in "${DISKS[#]}" ;
do
STATUS=`diskutil info /dev/disk$i | grep SMART | awk '{ print $3 }'`
if [ "$STATUS" = "Verified" ]; then
echo "SMART STATUS OK FOR DISK $i" >> $LOGS
else
echo "** SMART STATUS $STATUS FOR DISK $i **" >> $LOGS
fi
done
echo " " >> $LOGS
echo " " >> $LOGS
For example:
$ diskutil info /dev/disk8 | grep SMART
SMART Status: Not Supported
$ diskutil info /dev/disk2 | grep SMART
SMART Status: Verified
Ideally the script would echo "Not Supported" for cases like disk8. I think the value for "SMART Status:" may vary between one word and several. I don't see all the possibilities on the man page, so I don't have a definitive answer for this.
You can save the grep, and put all things in awk:
try this:
STATUS=$(diskutil info /dev/disk$i|awk -F': *' '/SMART/{print $2}')
Is this fits your needs ?
disks="0 1 2 3 4 5 6 7"
for i in $disks; do
status=$(diskutil info /dev/disk$i | awk '/SMART/{$1=$2="";print}')
if [[ $status == *Verified* ]]; then
echo "SMART status OK for disk $i"
else
echo "** SMART status $status for disk $i **"
fi
done
cut solution
If you can rely on the number of spaces printed by diskutil, you can just begin cutting at the first char of the status:
STATUS=`diskutil info /dev/disk$i | grep SMART | cut -c 30-`
awk solution
The word Supported is caught as $4, so we can use an if statement to check, whether the word is filled:
STATUS=`diskutil info /dev/disk$i | grep SMART | awk '{ if($4 != "") print $3,$4; else print $3; }'`
If you have GNU grep, you can do:
STATUS=$(diskutil info /dev/disk$i | grep -oP '(?<=SMART Status:).*')
The contents of the variable will contain leading whitespace. However, your if condition can accomodate that:
shopt -s extglob
if [[ ${STATUS##*([[:space:]])} == Verified ]]; then

Resources