5

I have a txt file that contains some numbers like this:

1  
2  
3  
4  
5  

And I have another txt file that contains the same number of lines, but with other numbers:

6  
7   
8  
9  
10

I want to add them together, namely 1+6, 2+7, 3+8, etc.. How do I write the script?

By the way, I've got a variety of answers so far, and only after I tried them on my files did I realise some of the methods can't deal with decimals. Some of my files contain decimals, and I need to be accurate, so if you would like to add an answer, could you show a method that can calculate decimals accurately. Thanks.

  • 3
    Do you really want to do this "with Bash" - or are you looking for a command line solution more generally? – steeldriver Feb 19 at 1:21
  • 2
    You will get a much more friendly reception and much better help here if you show what code you have tried so far and describe what problems you were having with it. Without code, your question looks like a request for free consulting and many people don't like that. – John1024 Feb 19 at 1:28
  • @steeldriver Not sure what you mean, but I'm trying to write a Bash script to do a series of tasks, amongst which is this one, so I need a Bash command to do this rather than any other language. – OhLook Feb 19 at 2:11
  • @John1024 I tried no code 'cos I didn't know which command I should use, and that's why I came here to ask. – OhLook Feb 19 at 2:13
  • It's not apparent from your question that the numbers in the files could be decimals; perhaps the sample input should reflect the types of data you're facing. – Jeff Schaller Feb 21 at 1:07
9

This is basic task many tools can solve; paste + awk combo seems exceptionally handy:

$ paste file1 file2 | awk '{$0=$1+$2}1'
7
9
11
13
15
  • I like this answer. It allows me to do decimals, and allows me to do other types of arithmetic. But I wonder what 1 after the } means? – OhLook Feb 20 at 22:09
  • 1
    @OhLook The "1" forces printing all lines. The awk performs actions inside {} if conditions are met, e.g.$1>4{print}, but print is a default action so one can omit it. In other words, preudo code is $0=$1+$2; if(true) print $0. In fact awk '$0=$1+$2' would work too if only two lines don't sum up to 0. – jimmij Feb 21 at 9:24
12

Along the paste lines, but doing the math with bc:

$ paste -d+ file1 file2 | bc
7
9
11
13
15

The intermediate result (before bc):

$ paste -d+ file1 file2
1+6
2+7
3+8
4+9
5+10

For a more bash-centric solution, and assuming that file2 has at least as many lines as file1:

mapfile -t file1 < file1
mapfile -t file2 < file2
for((i=0; i < ${#file1[@]}; i++))
do
  printf '%d\n' $((file1[i] + file2[i]))
done

... and for non-whole numbers, combine the ideas:

mapfile -t file1 < file1
mapfile -t file2 < file2
for((i=0; i < ${#file1[@]}; i++))
do
  printf '%d + %d\n' "${file1[0]}" "${file2[0]}" | bc
done
  • Thanks but the paste command doesn't work for me. It says (standard_in) 1: illegal character: ^M. I don't know what it means 'cos there's no ^M in my files. I'll give mapfile a go... – OhLook Feb 19 at 2:08
  • 4
    That's an indication that you have a DOS/Windows format file with CRLF line endings instead of just CR. Transfer it differently or re-save it, or post-process it: unix.stackexchange.com/a/192093/117549 – Jeff Schaller Feb 19 at 2:13
  • 1
    I tried, but instead of what you said, the other way round works. You said I should change CRLF into CR, but CR doesn't work either. I changed it into LF, and it works. – OhLook Feb 19 at 4:19
  • I tried your mapfile method, and I find one problem. When there are only integers, it works well, but some of my files have decimals, and it shows an error message: syntax error: invalid arithmetic operator. Although I added | bc after that, it still shows the same error message. Do you know how to do decimals in this case? – OhLook Feb 20 at 21:15
  • paste -d+ file1 file2 | bc is able to do decimals, but that mapfile for loop can't. – OhLook Feb 20 at 22:17
6

an awk-only solution

awk '(getline a <"file2") <= 0 {exit}; {print $0 + a}' file1
  • 1
    +1 That one has the advantage of supporting floating point numbers (and hex numbers in some awk implementations) and also of not causing much harm if the input files are not guaranteed to contain numbers (the other answers using shell arithmetic have ACE vulnerabilities). It would also work around the OP's CRLF line ending issue. – Stéphane Chazelas Feb 19 at 17:08
1

Here's a bash only solution (no other executables used):

while read X; do read Y <&3; echo $((X + Y)); done <file1 3<file2

In this, file1 is redirected into standard input, and file2 is redirected into an arbitrary file descriptor, 3.

The while then reads lines from file1 into X and, for each iteration, explicitly reads a line from file descriptor 3 (file2) and then performs the calculation using shell arithmetic.

0

Ok, it's a little cryptic, but also with bash arithmetic, paste and sed.

$ # debug
$ paste -d+ <(sed 's/\(.*\)/echo $((\1/' file1) <(sed 's/\(.*\)/\1))/' file2)
echo $((1+6))
echo $((2+7))
echo $((3+8))
echo $((4+9))
echo $((5+10))

$ eval "$(paste -d+ <(sed 's/\(.*\)/echo $((\1/' file1) <(sed 's/\(.*\)/\1))/' file2))"
7
9
11
13
15
0

Overly complicated Bash only solution

for file /tmp/file1

1
2
3
4
5

and /tmp/file2

6
7
8
9
10

write the following script.sh

#!/bin/bash
# counters
n=0
m=0
i=0

# read every line from the first file
while read line1;
do
  declare firstfile[$n]=$line1
  n=$((n+1))
done <$1

# read every line from the second file 
while read line2;
do
  declare secondfile[$m]=$line2
  m=$((m+1))
done <$2

# combine them with arithemtic
while read foo;
do
  num=$((${firstfile[$i]} + ${secondfile[$i]}))
  echo $num 
  i=$((i+1))
done <$1

and execute it

#:~$ bash script.sh /tmp/file1 /tmp/file2
7
9
11
13
15

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.