0

I have a data file with some sample data (First Name, Last Name, Income, Rax rate). I need to do some calculations, but when displaying the result, I'm having trouble as it's a loop and it is not displaying in order. Yes, I must use bash.

Example file:

------------------------------------
example-file.txt:

John,Rambo,99880,2%
Elon,Musk,144000,2%
------------------------------------

test.bash:

#/bin/bash

FILE=example-file.txt
 
for file in $FILE;
do
    # Get income
    INCOME=$(cat $file | awk -F, '{print $3}');
    FIRSTNAME=$(cat $file | awk -F, '{print $1}');
    LASTNAME=$(cat $file | awk -F, '{print $2}');
    # Get tax rate
    RATE=$(cat $file | awk -F, '{print $4}' | sed 's/%//');

    for foo in $INCOME
    do
     if [ "$foo" -ge 80000 ] && [ "$foo" -lt 150000 ];
        then
            for faa in $foo
            do
                NETINCOME=$(printf "%'.f\n" $(echo "(($INCOME * $RATE / 100))" | bc))
            done
    fi
    echo "${FIRSTNAME} ${LASTNAME} ${NETINCOME}"
   done
done

Output:

(standard_in) 2: syntax error
(standard_in) 3: syntax error
John
Elon Rambo
Musk 288,000
(standard_in) 2: syntax error
(standard_in) 3: syntax error
John
Elon Rambo
Musk 288,000

Desired output:

John Rambo 1997
Elon Musk 2880

How can I achieve that?

2 Answers 2

4

Since you are using awk to extract fields from your lines, why don't you just let it do all the work? It should have sufficient arithmetic capabilities and converting your script into an AWK script may be easier than fixing it.

$ cat example-file.txt 
John,Rambo,99880,2%
Elon,Musk,144000,2%
$ awk -v FS=, '{ print $1,$2,sprintf("%i", $3 * $4 / 100) }' example-file.txt 
John Rambo 1997
Elon Musk 2880

Use the first argument given to sprintf to adjust the format of the printed number.

You can also filter lines. For instance, assuming you are only interested in an income range:

$ awk -v FS=, '
  80000 < $3 && $3 < 150000 {
    print $1,$2,sprintf("%i",$3 * $4 / 100)
  }'

It accepts an arbitrary number of files as arguments (the upper bound depending on your system configuration; if you have a lot of them, use find ... -exec awk ...):

awk -v FS=, '...' file1.txt file2.txt ...

Finally, you can save the script as a file, without the enclosing 's, and invoke it as:

awk -v FS=, -f awk_script file1.txt

removing the need for inconvenient string concatenations in case you needed to use literal 's somewhere. For example, if you wanted the sprintf format string to include the ' flag, the inline script would become:

awk ... '... sprintf("%'"'"'.f", $3 * $4 / 100) ...' file
2
  • There are some other calculations I'm doing that I didn't put in the original question. Is it possible to use BC with that solution you provided? if yes, could you give me an example, please?
    – Manuela
    Commented Sep 24, 2020 at 9:54
  • 2
    @Manuela if you don't need to use bash as the question stated, then you really, really should do as fra-san suggested. Bash (shells, in general) are horrible tools for text parsing. As for bc, there is no need for it, awk can do floating point math.
    – terdon
    Commented Sep 24, 2020 at 10:01
2

Your script has several issues. First, you are treating strings as arrays (there's no reason or point in doing for foo in $bar when $bar is just one string). Second, you are reading the file multiple times when all you need to do is read it once. Third, you are not quoting your variables, which means the script will break if the file names can contain whitespace. Here's a better way:

#/bin/bash

## take a list of file names as an argument so you can
## work on multiple files.
for file in "$@";
do

  ## get the values
  while read firstName lastName income  rate; do
    rate=${rate//%}
    if [[ "$income" -ge 80000  && "$income" -lt 150000 ]]; then
      netIncome=$(printf "%'.f\n" $(echo "($income * $rate) / 100" | bc))
    ## You didn't specify what should happen if the income isn't in the
    ## target range, so I am assuming you want it to be the $income unchanged.
    else
      netIncome=$income
    fi
    echo "$firstName $lastName $netIncome"
  done < <(awk -F, '{print $1,$2,$3,$4}' "$file")
done

You can run this with the file(s) as arguments:

$ foo.sh file
John Rambo 1,997
Elon Musk 2,880
2
  • Thanks. Getting this error when executing it: ./test22.sh: line 22: syntax error near unexpected token <'` ./test22.sh: line 22: done < <(awk -F, '{print $1,$2,$3,$4}' "$file")'`
    – Manuela
    Commented Sep 24, 2020 at 10:08
  • 1
    @Manuela how are you running it? Are you maybe doing sh foo.sh file instead of foo.sh file or bash foo.sh file? This is a bash script, so you need to use bash, not sh.
    – terdon
    Commented Sep 24, 2020 at 10:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .