0

Supposedly this is my data:

Lord of The Ring:Johnny Dept:56.80:100:38
Three Little Pig:Andrew Lim:89.10:290:189
All About Ubuntu:Ubuntu Team:76.00:55:133
Catch Me If You Can:Mary Ann:23.60:6:2
Happy Day:Mary Ann:12.99:197:101
Harry Potter:J.K Rowling:23:32:421
jo:jojo:2000:120:293

the code:

function search_book
{
    echo "4) search_book"
    read -p "Title: " title
    read -p "Author: " author

    searchtitle=` grep -i "^$title:$author:*" BooksDB.txt | cut -d: -f 1 `
    searchauthor=` grep -i "^$title:$author:*" BooksDB.txt | cut -d: -f 2 `
    #Data does not compute to true if two variables are filled and exist if not typed fully
    if [[ $searchtitle == $title ]] || [[ $searchauthor == $author ]]
    then
    recordcount=`cat | grep -iE "*$title" BooksDB.txt | grep -iEc "$author" `
    echo "Found $recordcount records"
    echo 
    grep -iE "*$title" BooksDB.txt | grep -iE "$author" |\
        awk -F: 'BEGIN{print "Title:Author:Price:Available Copies:Sold Copies\n"}
        {print $1":"$2":$"$3":"$4":"$5}' | column -s ":" -t
    echo 
    else
    echo "No Books found."
    fi

}

Program's output:

4) search_book
Title: jo
Author: 

Found 2 records

Title             Author       Price   Available Copies  Sold Copies
Lord of The Ring  Johnny Dept  $56.80  100               38
jo                jojo         $2000   120               293

When my title is jo, only the second record is supposed to be the result but where and why does it made it such that it searches both title and author altogether?

Expected result:

4) search_book
Title: jo
Author: 

Found 2 records

Title             Author       Price   Available Copies  Sold Copies
jo                jojo         $2000   120               293
J.Lene
  • 27
  • 2
  • post the expected result – RomanPerekhrest Jan 30 '18 at 09:42
  • Hi there! Thanks for replying, my expected result is to just show "jo" instead of the 2 results. I have edited accordingly. @RomanPerekhrest – J.Lene Jan 30 '18 at 10:23
  • This looks like [recurring homework](https://www.google.com/search?q=%22johnny+dept%22+site%3Astackoverflow.com). Does your professor approve of your use of Stack Overflow to solve your problem? Have you examined the questions from previous years with similar problems? – tripleee Jan 30 '18 at 10:31
  • This is a typical `awk` problem. Something like `awk -v a="Andrew" -v t="Little" -v FS=":" '$2 ~ a && $1 ~ t' file` would save you a lot of time. – sjsam Jan 30 '18 at 10:36
  • voting to close: the OP didn't provide any reaction at his similar previous question – RomanPerekhrest Jan 30 '18 at 10:39

6 Answers6

1

Just use awk, this should get you started:

awk -F: -v title="$title" author="$author" \
    'BEGIN { print "Title:Author:Price:Available Copies:Sold Copies\n" }
     $1 ~ $title && $2 ~ $author' BooksDB.txt OFS=:
Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
0

The way you have implemented the search, it is a bit tricky to get it right. Try:

function search_book
{
    echo "4) search_book"
    read -p "Title: " title
    read -p "Author: " author

    searchtitle=` grep -i "^$title" BooksDB.txt | cut -d: -f 1 `
    searchauthor=` grep -i "^[^:]+:$author" BooksDB.txt | cut -d: -f 2 `
    #Data does not compute to true if two variables are filled and exist if not typed fully
    if [[ $searchtitle == $title ]] || [[ $searchauthor == $author ]]
    then
    recordcount=`grep -iE "^$title" BooksDB.txt | grep -iEc "^[^:]+:$author" `
    echo "Found $recordcount records"
    echo
    grep -iE "^$title" BooksDB.txt | grep -iE "^[^:]+:$author" |\
        awk -F: 'BEGIN{print "Title:Author:Price:Available Copies:Sold Copies\n"}
        {print $1":"$2":$"$3":"$4":"$5}' | column -s ":" -t
    echo
    else
    echo "No Books found."
    fi
}

Then, if you run:

4) search_book
Title: jo
Author: .*

Found 1 records

Title  Author  Price  Available Copies  Sold Copies
jo     jojo    $2000  120               293

Your search patterns are all too broad. The initial one is nearly right, but then the others do not make sure the right field is matched at the right place. For instance, for the second field:

^[^:]+:$author

Will first skip a field searching for anything but : from the beginning of the line, then the :, then the $author variable. If you want to allow an empty title, use [^:]* instead.

Also, you don't state whether you mean to implement an and or an or. The way I have left it, it is an and and the field needs to match from the beginning but not all the way (seems to be what you want.)

In case you want an empty search field to match anything, you may add a condition after reading the input to turn an empty field into .*.

Javier Elices
  • 2,066
  • 1
  • 16
  • 25
  • The `cat` is not only [useless](https://stackoverflow.com/questions/11710552/useless-use-of-cat), it looks like an outright bug. – tripleee Jan 30 '18 at 10:33
  • If you are calling Awk anyway, `grep -i x y | awk '{z}'` should probably be refactored to `awk 'tolower($0) ~ /x/ {z}' y` – tripleee Jan 30 '18 at 10:35
  • I would not implement it like this. It is unnecessarily complicated. I am trying to make minimal changes for the code to work in order to illustrate what is wrong. That said, any comments to improve are welcome! – Javier Elices Jan 30 '18 at 10:39
  • @tripleee, I agree with the `cat`. Does not look good. I have removed it. There is a lot of useless code, but it seems to work... – Javier Elices Jan 30 '18 at 10:42
  • Using the logic, I did: searchtitle=` grep -i "^$title+^[^:]+" BooksDB.txt | cut -d: -f 1 searchauthor=` grep -i "^[^:]+:$author" BooksDB.txt | cut -d: -f 2 ` worked exactly for what I wanted, thank you! – J.Lene Jan 30 '18 at 11:04
  • @J.Lene, glad to hear so. If my answer was useful, consider marking it as correct or upvoting it... :-) – Javier Elices Jan 30 '18 at 15:57
0

Here's how I would implement the searches. Obviously you run the search on title only if the user entered a title and so on.

Search for title

sed -n -E '/^.*jo.*(:.*){4}$/Ip' file

Search for author

sed -n -E '/^.*:.*jo.*(:.*){3}$/Ip' file

This tries to match the pattern on each line (case insensitive) and prints the line if the pattern is found.

eric.v
  • 605
  • 5
  • 9
  • I was thinking of being able to search 3 ways Title and empty (field) Author Author and empty Title Both author and title filled – J.Lene Jan 30 '18 at 10:56
0

awk is your friend here. Do something in similar terms.

$ awk -v t="$title" -v a="$author" -v FS=":" '
BEGIN{count=0;printf "Title:Author:Price:Available Copies:Sold Copies%s",ORS}
{flag=0}
$2 ~ a || $1 ~ t{flag=1;count++}flag
END{printf "Total Records - %s%s",count,ORS}' 48518186 | column -s':' -t
# Above, $author & $title are the variables you read using 'read'

Output

Title              Author  Price  Available Copies  Sold Copies
jo                 jojo    2000   120               293
Total Records - 1
sjsam
  • 21,411
  • 5
  • 55
  • 102
0

Your code prints both entries, because you are not specifying an "Author" and as such the 2nd grep in grep -iE "^$title" BooksDB.txt | grep -iE "^[^:]+:$author" is not filtering anything. I would suggest using awk since your file contains a : inherent delimiter and this would make your search precise.

Please note, if you do not specify either the Author or Title filed, it will not use that field as a filter.

Example:

function search_book
{
    echo "4) search_book"
    read -p "Title: " title
    read -p "Author: " author

    #searchtitle=` grep -i "^$title:$author:*" BooksDB.txt | cut -d: -f 1 `
###by using awk instead you only look for a title in the title column
    searchtitle=`awk -F':' -v t=$title 'BEGIN{IGNORECASE = 1} $1 ~ t {print $1}' BooksDB.txt`
    #searchauthor=` grep -i "^$title:$author:*" BooksDB.txt | cut -d: -f 2 `
    searchauthor=`awk -F':' -v a=$author 'BEGIN{IGNORECASE = 1} $2 ~ a {print $2}' BooksDB.txt`
    #If both searchtitle and searchauthor vars are empty, just say "No Books found."
    if [[ ! -z $searchtitle ]] && [[ ! -z $searchauthor ]]
    then
    recordcount=`awk -F':' -v t="$title" -v a="$author" 'BEGIN{IGNORECASE = 1} $1 ~ t && $2 ~ a {cnt++} END {print cnt}' BooksDB.txt`
    echo "Found $recordcount records"
    echo
    awk -F':' -v t="$title" -v a="$author" 'BEGIN{IGNORECASE = 1; print "Title:Author:Price:Available Copies:Sold Copies\n"}; $1 ~ t && $2 ~ a {print $0}' BooksDB.txt | column -s ":" -t
    echo
    else
    echo "No Books found."
    fi

}
search_book
AnythingIsFine
  • 1,777
  • 13
  • 11
0
searchtitle=` grep -i "^$title+^[^:]+" BooksDB.txt | cut -d: -f 1 `
searchauthor=` grep -i "^[^:]+:$author" BooksDB.txt | cut -d: -f 2 `
Armali
  • 18,255
  • 14
  • 57
  • 171
J.Lene
  • 27
  • 2
  • 1
    Even if this answer is "correct", it will be flagged as "low quality" on this site because it does not include textual explanation of how it works or why the OP's code does not work. – Jonathan Ben-Avraham Jan 30 '18 at 13:58