Classified
Classified

Reputation: 6050

How to read content from two files and merge into a 3rd file in bash shell

How do you read/process 2 files in sync with each other in bash?

I have 2 text files which have the same number of lines/items in them. One file is

a
b
c

The other file is

1
2
3

How do I loop through these files in sync so that a is associated with 1, b->2, c->3?

I thought that I could read in the files as an array and then process them with an index but it seems like my syntax/logic is incorrect.

So doing f1=$(cat file1) makes f1 = a b c. I thought doing f1=($(cat file1)) would make it into an array but it makes f1=a and thus no array for me to process.

In case anyone was wondering what my messed up code is:

hostnames=($(cat $host_file))  
# trying to read in as an array, which apparently is incorrect
roles=($(cat $role_file))

for i in {0..3}
do
   echo ${hostnames[$i]}   
   # wanted to iterate through each element in the file/array
   # but there is only one object instead of N objects
   echo ${roles[$i]}
done

Upvotes: 8

Views: 691

Answers (7)

Chris Seymour
Chris Seymour

Reputation: 85775

A concise and flexible solution to this problem is the core-util pr:

# space separated
$ pr -mts' ' file1 file2
a 1
b 2
c 3

# -> separated
$ pr -mts' -> ' file1 file2
a -> 1
b -> 2
c -> 3

See man pr for more information.

Upvotes: 2

Fritz G. Mehner
Fritz G. Mehner

Reputation: 17188

Pure Bash:

IFS=$'\n'
hostnames=( $( <hostnames.txt ) )
roles=( $( <roles.txt ) )

for idx in ${!hostnames[@]}; do    # loop over array indices
  echo -e "${hostnames[idx]} ${roles[idx]}"
done

or after gniourf_gniourf's comment

mapfile -t hostnames < hostnames.txt
mapfile -t roles < roles.txt

for idx in ${!hostnames[@]}; do              # loop over array indices
  echo -e "'${hostnames[idx]}' '${roles[idx]}'"
done

Upvotes: 0

michael501
michael501

Reputation: 1482

Your way:

host_file=host1
role_file=role1

hostnames=(  $(cat $host_file) )  
roles=( $(cat $role_file)  )
(( cnt = ${#hostnames[@]}  -1 ))
echo "cnt is $cnt"
for (( i=0;i<=$cnt;i++))
do
  echo "${hostnames[$i]} ->    ${roles[$i]}"
done

Upvotes: 2

Endoro
Endoro

Reputation: 37569

two examples with :

awk '{print $0, NR}' file1

and - much better :-)

awk 'NR==FNR {a[NR]=$0;next};{print a[FNR], $0}' file1 file2

..output is always:

a 1
b 2
c 3

Upvotes: 2

captcha
captcha

Reputation: 3756

Code for GNU :

  • with file1 in front:

    sed -r 's#(.*)#s/(.*)/\1 \\1/;$!n#' file1|sed -rf - file2
    

    or

  • with file2 in front:

    sed -r 's#(.*)#s/(.*)/\\1 \1/;$!n#' file2|sed -rf - file1
    

Both leads to the same output:

a 1
b 2
c 3
d 4
e 5
f 6
g 7

Upvotes: 2

chirlu
chirlu

Reputation: 3301

Use paste (invocation) to combine the files, then process one line of the combined file at a time:

paste file1 file2 |
while read -r first second
do
  echo $first
  echo $second
done

Upvotes: 11

jaypal singh
jaypal singh

Reputation: 77085

You can use file descriptors:

while read -r var_from_file1 && read -r var_from_file2 <&3; do 
    echo "$var_from_file1 ---> $var_from_file2"
done <file1 3<file2

Output:

a ---> 1
b ---> 2
c ---> 3

Upvotes: 17

Related Questions