-4

Let’s imagine we have a budget document: budget.txt

The exercise is to calculate the sum column for every product in the month, also to calculate totals in the last row. You may try to do it in a shell. How shall it work?

  • a program/script shall take a parameter with the filename (not to be hardcoded in the body)
  • do the needful
  • print output to a screen
  • if there’s an error in the data file somewhere, the program/script shall print it on a screen and terminate.

For the bash script, you may try with awk and bc commands.

enter image description here

I have created a shell script

#!/bin/bash
echo -n " Please write the file name:" 
read file 
awk '{print $0}' $file
James Z
  • 12,209
  • 10
  • 24
  • 44
Aktar
  • 11
  • 3
  • I have attached the budget.txt file in.jpeg format – Aktar Aug 19 '21 at 19:07
  • 2
    So, that's your homework. Have you done something? Do you have problem? or a question? – James Z Aug 19 '21 at 19:34
  • Hi James, I have tried to do it but as I know basic I couldn't find the solution. I can take the data from the .txt file and print using awk but don't know how to sum the rows and show them in Sum Column – Aktar Aug 19 '21 at 19:45
  • I have created a shell script #!/bin/bash echo -n " Please write the file name:" read file awk '{print $0}' $file – Aktar Aug 19 '21 at 19:46
  • 2
    If you [edit] your question to contain a [mcve] with concise, testable sample input and expected output plus your attempt to solve the problem yourself then we can help you. Make sure your question just contains [well-formatted](https://stackoverflow.com/help/formatting) text - no images, no links, just text. See [ask] and look at some existing questions that have been upvoted and answered if that's not clear. – Ed Morton Aug 19 '21 at 20:14
  • @Aktar Something that might be helpful for learning on this site would be to try to do as much of the problem you can, and then if you need to ask how to do something, or get stuck with an error, try to fix it, then look to see if it's been answered, and if not, ask it and share what you tried! A good programmer needs dedication and determination more than direct homework answers, you can do it! – G.Mart Aug 20 '21 at 03:42
  • Thank you so much. I am trying my best – Aktar Aug 22 '21 at 19:29

2 Answers2

1

To demonstrate what Ed was talking about when requesting text ... XOne working solution

And now a pretty-printed, annotated version of the awk bit ...

NR == 1 {                              # print the header line (the 1st row/record)
        print
}

NR > 1 && $0 != "Total" {              # for all but the last record (which starts with "Total")
        rsum = 0                       # zero a tally for the entire row
        for (i = 2; i <= NF; i++) {    # iterate over every column (field) from the 2nd to the last (NF)
                a[i] += $i             # add each fields value to an array (representing the column)
                rsum += $i             # add each fields value to the sum for the row
        }
        printf "%s\t%3d\n", $0, rsum   # print each row with the sum of the row
        fc = NF                        # store the number of fields for the next operation
        a[NF + 1] += rsum              # and tally the grand total
}

$1 == "Total" {                        # for the last row
        printf "Total\t"               # print "Total"
        for (i = 2; i <= fc; i++) {    # then the array values we added up above
                printf "%3d\t", a[i]
        }
        printf "%3d\n", a[fc + 1]      # followed by the grand total
}
tink
  • 14,342
  • 4
  • 46
  • 50
  • Hi tink: Could you please check if i wrote the code correctly? awk 'NR==1{print} NR>1 && $0!="Total" {rsum=0;for(i=2;i<=NF;i++){a[i]+=$i;rsum+=$i};printf "%s\t%3d\n", $0,rsum:fc=NF;a[NF+1]+=rsum} $1=="Total" {printf "Total\t";for(i+2;i<=fc;i++) {printf "%3d\t",a[i]);printf "%3d\n",a[fc+1])}' – Aktar Aug 20 '21 at 18:45
  • `awk 'NR==1{print} NR>1 && $0!="Total"{rsum=0;for(i=2;i<=NF;i++){a[i]+=$i;rsum+=$i};printf "%s\t%3d\n", $0,rsum;fc=NF;a[NF+1]+=rsum} $1=="Total"{printf "Total\t";for(i=2;i<=fc;i++){printf "%3d\t",a[i]};printf "%3d\n",a[fc+1] }' budget.txt` @Aktar ... and please, **don't** post images of text :P – tink Aug 20 '21 at 18:59
  • What if I want to run it in a script so I can do something like that that echo -n "Enter the file name: " read file_name awk.................. at end $file_name – Aktar Aug 20 '21 at 19:18
  • @Aktar Same thing. Just replace the `budget.txt` with your `$file`. – tink Aug 20 '21 at 19:20
  • you are awesome. I really appreciate your time and support. Can I have your LinkedIn or Facebook? I want to learn bash shell-scripting – Aktar Aug 20 '21 at 19:22
  • Yeah nah, sorry, I prefer to help people here (in my own good time and without expectations). :) But thanks for the praise, and feel free to accept my answer if it does what you want. @Aktar – tink Aug 20 '21 at 19:23
  • All works except I got the result: Butter 4 5 8 11 36 2 66 Total 37 66 57 87 68 87 402 something wrong in the last row – Aktar Aug 20 '21 at 20:55
  • What did you expect, @Aktar? To me it looks perfectly fine, 402 is the sum for both the last column and the last row, in other words the total spend of all foods over all six months ... – tink Aug 20 '21 at 23:59
  • I expect the Total row shows the sum of one number then show tab then another data. not shows nicely sequencewise – Aktar Aug 21 '21 at 16:04
  • Sorry, I don't understand what you mean ... you want two identical numbers to show in different places? What do you mean by "show tab" @Aktar? – tink Aug 21 '21 at 18:40
  • I can't add the screenshot to show you my output. can you give me your mail address so that I can sent you the screenshot that might helpful to understand my issue – Aktar Aug 22 '21 at 19:30
  • Just upload to [this](https://pasteboard.co/) and paste the link @Aktar – tink Aug 22 '21 at 19:53
  • How on earth did you achieve **that**? Can you please paste the actual text of your budget.txt to a public pastebin? I obviously had to **TYPE** your values from the screenshot of your above; and that renders the screenshot I pasted. I have no explanation for that latest image of yours. @Aktar – tink Aug 22 '21 at 20:58
0

You can try this code which is not full proof. But you can make the amendment on this code as per your future requirement. At the moment I have created the budget.txt file and make sure that all the content on it are separated by Tab. After running this script it is providing the below results.

enter image description here

This is the code sample (Note: No value is hard-coded on this script):

#!/bin/bash

function main {
    file_check
    file_content
    calculation
    exit 0
}

function file_check {
    # Asking to provide the file name or complete path. 
    read -p "Enter the file name: " file_name
    
    # Checking if the file exist else exit the script.
    [ -f "$file_name" ] || { echo "The provided file name does not exist. Terminating the script."; exit 0; }
}

function file_content {
    # Saving the file content of first row and column in arrays. 
    column=($(awk '{ print $1 }' "$file_name"))
    row=($(awk 'FNR == 1 { print }' "$file_name"))
    # Calculating the length of the variables.
    column_length=${#column[@]}
    row_length=${#row[@]}
}

function calculation {
    # To calculate the sum of every item per month. 
    for((i=2;i<$row_length;i++)); do
        num=($(awk -v var="$i" '{print $var}' $file_name | sed '1d;$d'))
        sum=$(IFS=+; echo "$((${num[*]}))")
        search_replace_row
    done
    
    # To calculate the last sum of total.
    num=($(tail -1 $file_name | awk '{$1=""}1'))
    sum=$(IFS=+; echo "$((${num[*]}))")
    search_replace_row

    # To calculate sum of single item for every month.
    for ((i=2;i<$column_length;i++)); do
        num=($(awk "FNR == $i { print }" budget.txt | awk '{$1=""}1'))
        sum=$(IFS=+; echo "$((${num[*]}))")
        search_replace_column
    done
}
function search_replace_row {
    content_row=$(tail -1 "$file_name")
    sed -i "s/$content_row/$content_row\t$sum/" $file_name
}
function search_replace_column {
    content_column=$(sed -n "${i}p" $file_name)
    sed -i "s/$content_column/$content_column\t$sum/g" $file_name
}


main