0

I'm trying to divide all lines in file1.txt by their respective (column-wise) value in the single line in file2.txt.

cat file1.txt

1       2.5     3
7       7       7
1       3       5

cat file2.txt

1   3   5

Following the suggested solution for this question: https://stackoverflow.com/questions/44908195/awk-multiplication-of-all-rows-in-a-table-with-first-row-of-the-table, I came up with the following code:

cat file2.txt file1.txt | awk 'NR==1{split($0,m);CONVFMT="%.5f\t";next} {for (i=1;i<=NF;i++) $i=$i/m[i]} 1'

However, since all values in one of the rows equal 1, and CONVFMT doesn't work on integers, the format in my output file is messed up. To fix this, I was thinking on using printf with tab separator instead of CONVFMT, but given that my actual files have a variable number of columns, I don't want a hard coded solution using $1, $2, etc. I'm not proficient in awk so I can't quite come up with a fix on my own.

Thank you very much in advance for your help!

Edit: all numbers in output should be formatted as %.5f.

  • 1
    Welcome to the site. Can you edit your post to specify the desired output for the sample input you provided? Is it "5 decimals for fractions, none for integers"? – AdminBee Apr 23 at 13:22
2

If you want all fields formatted as %.5f, you could use sprintf:

BEGIN {
    OFS = "\t"
}

NR == 1 {
    cols = split($0,m)
    next
}

NF == cols {
    for (i=1; i<=NF; i++)
        $i = sprintf("%.5f", $i/m[i])
}

1

$ awk -f above.awk file2 file1
1.00000 0.83333 0.60000
7.00000 2.33333 1.40000
1.00000 1.00000 1.00000

The above awk program doesn't warn about possible errors. You could try:

NR == 1 {
    cols = split($0,m)
    for (i in m)
        if (m[i] == 0)
            err("field "i" is "m[i]"; division by zero is fatal", 1)
    next
}

NF != cols {
    err("found "NF" fields, expected "cols)
    next
}

{
    for (i in m)
        $i = sprintf("%.5f", $i/m[i])
    print
}


END {
    exit errs
}

function err(msg, r) {

    # Print message to stderr
    # Leave non-zero exit status
    # Optionally go to END

    printf "%s - %s.\n", "error:  line "FNR" in "FILENAME, msg | "cat >&2"
    errs = 1
    if (r) exit
}

Also, you may want to check that each field is a number: Make awk produce error on non-numeric; Can I determine type of an awk variable?

| improve this answer | |
  • Thank you very much, this is exactly what I wanted and works perfectly! :) – Daniel Kleinman Ruiz Apr 23 at 13:57
  • 2
    Just a suggestion to do an error check rather than just ignore any mismatches NF!=cols{ print "Wrong number of fields at row "FNR" in "FILENAME; next}{for (i=1; i<=NF; i++) – bu5hman Apr 23 at 15:45
  • 2
    It might be worth checking for m[i] being non-zero before dividing by it. Also print > "/dev/stderr" will only work in some awks, print | "cat>&2" will work in all of them. – Ed Morton Apr 24 at 0:13
1

Unfortunately, the desired output format is not quite clear from your question.

However, as a general rule, if you want to ensure "table-formatted output", you could try piping the result to column:

awk 'BEGIN{CONVFMT="%.5f"} FNR==NR{split($0,div);next} NR>FNR{for (i=1;i<=NF;i++) $i=$i/div[i];}1' file2.txt file1.txt | column -t

which will result in

1  0.83333  0.60000
7  2.33333  1.40000
1  1        1

for the sample input you provided.

As a side note, you never need to use cat when processing files with awk (or sed etc.)

Update

I see from your edit that all numbers are to represented as 5-decimal floating-point numbers. In that case, the solution by @guest is the right way, although the use of column may still be helpful if at any time in the future you want to print column titles, too.

| improve this answer | |
  • Thank you very much, I can confirm it works. I had tried piping the output of awk to column -t but I got an extra tab at the end of all rows with floating-point numbers which didn't appear in the integer row. Now I realise it's because I included a tab within CONVFMT. Also thanks for the reminder about cat & awk. I always forget about it. Much appreciated! – Daniel Kleinman Ruiz Apr 23 at 14:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.