-3

I want the variable $cpySold to be subtracted from $a[3] and added to $a[4]. How can I do it?

Currently my output is as follows:

Title:Alice in wonderland
Author:robert
No Of Copies Sold:*3*

Current Book Info:
Alice in wonderland, robert,$12.40,100,200

How do I do the line below? Assuming 100-3 =97, 100+3 = 103 after user entered 3 copies sold.

New Book Info: Alice in wonderland, robert,$12.40,97,203

function process_book_sold
{
    read -p "Title: " title
    read -p "Author: " author
    read -p "No Of Copies Sold : " cpySold 
    if [ -n "$title" -a -n "$author" ]; then
    perl -ne 'BEGIN{ $pattern = $ARGV[0]; shift;$pattern1 = $ARGV[0]; shift; $n=0 }
    @a=split /:/;
    if ($a[0] =~ m/$pattern/i and $a[1] =~ m/$pattern1/i) 
    {
         print "Current Book Info: \n";
         print "$a[0], $a[1],\$$a[2],$a[3],$a[4]\n";
    }
    END{ print "\n" }' "$title" "$author" /home/student/Downloads/BookDB.txt
    fi
}
RobEarl
  • 7,862
  • 6
  • 35
  • 50
  • 1
    why use bash and perl? Why not just use perl? – vol7ron Aug 04 '13 at 04:51
  • it is assignment requirement – Wen jie Chia Aug 04 '13 at 05:17
  • Give that the data contains 100 and 200, is the subtraction from the 100 and the addition to the 200, or the other way around? (...Hmmm...OK; the required output indicates subtract from 100 and add to 200...) Your code currently has the line `print "$a[0], $a[1], \$$a[2], $a[3], $a[4]\n";` which is puzzling: why the different treatment of `$a[2]`? – Jonathan Leffler Aug 04 '13 at 21:16

1 Answers1

0

I don't like the mix of shell and Perl in the code, but that's apparently for pedagogical reasons so we have to ignore it.

process_book_sold()
{
    read -p "Title: " title
    read -p "Author: " author
    read -p "No Of Copies Sold : " cpySold 
    if [ -n "$title" -a -n "$author" ]; then
        perl -ne '
            BEGIN{ $title = shift; $author = shift; $sales = shift; }
            @a = split /:/;
            if ($a[0] =~ m/$title/i and $a[1] =~ m/$author/i) 
            {
                 print "Current Book Info:\n";
                 print "$a[0], $a[1], $a[2], $a[3], $a[4]\n";
                 $a[3] -= $sales;
                 $a[4] += $sales;
                 print "New Book Info:\n";
                 print "$a[0], $a[1], $a[2], $a[3], $a[4]\n";
            }
            END{ print "\n" }' "$title" "$author" "$cpySold" /home/student/Downloads/BookDB.txt
    fi
}

Apart from renaming pattern to title and pattern1 to author, this code passes the shell variable $cpySold to the Perl. It also uses a simpler method of retrieving the first three arguments (simply capture the value from shift). The split is the same as before. It isn't entirely clear what the format in the data file is since the printed formats use commas rather than colons to separate the fields.


I simply want the values from new book info to replace current book info in the BookDB.txt file.

I'm not convinced this is doing you any favours (you won't learn much unless you try doing it yourself), but ...

process_book_sold()
{
    title="$1"
    author="$2"
    cpySold="$3"
    if [ -n "$title" -a -n "$author" ]
    then
        perl -i -we '
            use strict;
            use English "-no_match_vars";
            my $title = shift;
            my $author = shift;
            my $sales = shift;
            while (<>)
            {
                chomp;
                my @a = split /:/;
                print STDERR "Debug: @a\n";
                if ($a[0] =~ m/$title/i and $a[1] =~ m/$author/i) 
                {
                    print STDERR "Current Book Info:\n";
                    print STDERR "$a[0], $a[1], $a[2], $a[3], $a[4]\n";
                    $a[3] -= $sales;
                    $a[4] += $sales;
                    print STDERR "New Book Info:\n";
                    print STDERR "$a[0], $a[1], $a[2], $a[3], $a[4]\n";
                    $OFS = ":";
                    $ORS = "\n";
                    print @a;
                }
            }
            ' "$title" "$author" "$cpySold" BookDB.txt # /home/student/Downloads/BookDB.txt
    fi
}

#   read -p "Title: " title
#   read -p "Author: " author
#   read -p "No Of Copies Sold : " cpySold 
process_book_sold "Alice in Wonderland" "Carroll" "3"

This doesn't pester me with typing the title, author or number of copies sold. You can reinstate those lines if you wish, but the function is probably more useful if it takes the arguments. (It is often good to separate user interaction from code that operates on files.) I've used the correct author name (unless you want to use Dodgson as the real name of the author who used the pseudonym Lewis Carroll). The Perl script uses the -i option to overwrite the input files. It uses the English module so it can set $OFS and $ORS. It writes debug information to STDERR (otherwise, it would be part of the information written to the file).

When the file was called pbs2.sh, a sample run of the script looked like:

$ cat BookDB.txt; bash pbs2.sh; cat BookDB.txt
Alice in Wonderland:Carroll:$12.40:74:226
Debug: Alice in Wonderland Carroll $12.40 74 226
Current Book Info:
Alice in Wonderland, Carroll, $12.40, 74, 226
New Book Info:
Alice in Wonderland, Carroll, $12.40, 71, 229
Alice in Wonderland:Carroll:$12.40:71:229
$

Clearly, this wasn't the first time I'd run the script, and at times I used values other than 3 for the number of copies sold.

With explicit file management, you can write:

process_book_sold()
{
    title="$1"
    author="$2"
    cpySold="$3"
    if [ -n "$title" -a -n "$author" ]; then
        perl -we '
            use strict;
            use English "-no_match_vars";
            my $title = shift;
            my $author = shift;
            my $sales = shift;
            my $file = shift;
            open my $fh, "+<", $file or die "Failed to open file $file for reading and writing";
            my $text;
            {
            local $/;
            $text = <$fh>;
            }
            chomp $text;
            my @a = split /:/, $text;
            print "Debug: @a\n";
            if ($a[0] =~ m/$title/i and $a[1] =~ m/$author/i) 
            {
                print "Current Book Info:\n";
                print "$a[0], $a[1], $a[2], $a[3], $a[4]\n";
                $a[3] -= $sales;
                $a[4] += $sales;
                print "New Book Info:\n";
                print "$a[0], $a[1], $a[2], $a[3], $a[4]\n";
                seek $fh, 0, 0;
                truncate $fh, 0;
                $OFS = ":";
                $ORS = "\n";
                print $fh @a;
            }
            close $fh;
            ' "$title" "$author" "$cpySold" BookDB.txt # /home/student/Downloads/BookDB.txt
    fi
}

# read -p "Title: " title
# read -p "Author: " author
# read -p "No Of Copies Sold : " cpySold 

process_book_sold "Alice in Wonderland" "Carroll" "7"

Sample run:

$ cat BookDB.txt; bash pbs1.sh; cat BookDB.txt
Alice in Wonderland:Carroll:$12.40:50:250
Debug: Alice in Wonderland Carroll $12.40 50 250
Current Book Info:
Alice in Wonderland, Carroll, $12.40, 50, 250
New Book Info:
Alice in Wonderland, Carroll, $12.40, 43, 257
Alice in Wonderland:Carroll:$12.40:43:257
$
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • you have restricted scope of the variables initialized from the perl command line to the BEGIN block - and not enabled warnings or strict to notify you of the resulting problems. – ysth Aug 04 '13 at 21:31
  • More accurately, I've not run the code at all...but you're right. – Jonathan Leffler Aug 04 '13 at 22:30
  • @JonathanLeffler , sorry to bother u again, i will like to get the new book info and replace the current book info in the bookDB.txt, is that possible? – Wen jie Chia Aug 05 '13 at 04:42
  • You've not really shown what the format of the file looks like. In particular, does it contain a single record for the single book, or are there multiple records for multiple books? Yes, it is possible. There just isn't enough information in the question to make it clear what would make sense. (Also, the format is such that books with sub-titles don't store neatly — the colon separator for fields gets confused by colons between the title and sub-title. However, that's also presumably a given for the exercise.) – Jonathan Leffler Aug 05 '13 at 07:43
  • The bookDB.txt contain title:author:price:qtyAvailablr:qtySold – Wen jie Chia Aug 05 '13 at 09:49
  • The book will only have a single record – Wen jie Chia Aug 05 '13 at 09:50
  • So, the file only has a single record in it for a single book? That makes life easier. You still have to change the Perl code to explicitly open the file for reading and update (mode `+<` to `open`), read it in, update the data, and then truncate and rewind the file before writing the data back to the file (remembering to set the output field separator). You can see the relevant code in an answer to another question — [`sed` newline and carriage return pattern capture](http://stackoverflow.com/questions/18036557), where the recommended solution is to use Perl. – Jonathan Leffler Aug 05 '13 at 10:12
  • Sorry am new to perl can u teach me? I simply want the values from new book info to replace current book info in the bookdb.txt – Wen jie Chia Aug 05 '13 at 12:41
  • I pretty much spoon-fed you one answer above, and x-ref'd another question where some 'rewrite a file' code can be found. However, I'm about to update the answer with some code. – Jonathan Leffler Aug 05 '13 at 14:43