Как читать содержимое из двух файлов и объединяться в 3-й файл в оболочке bash

как Вы читаете / обрабатываете 2 файла в синхронизации друг с другом в bash?

у меня есть 2 текстовых файла, которые имеют одинаковое количество строк/элементов в них. Один файл

a
b
c

другой файл

1
2
3

как мне перебрать эти файлы в синхронизации, так что a связан с 1, b - >2, c->3?

Я думал, что могу читать файлы в виде массива, а затем обрабатывать их с помощью индекса, но похоже, что мой синтаксис / логика неправильный.

этом f1=$(cat file1) делает f1 = a b c. Я думал делать f1=($(cat file1)) сделал бы его в массив, но он делает f1=a и, таким образом, нет массива для обработки.

в случае, если кому-то было интересно, что мой испорченный код:

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

7 ответов


использовать paste (ссылка), чтобы объединить файлы, а затем обработать одну строку объединенного файла за раз:

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

можно использовать файловые дескрипторы:

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

выход:

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

код для GNU sed:

  • С file1 перед:

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

    или

  • С file2 перед:

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

оба вывода приводят к одному и тому же выводу:

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

два примера с awk:

awk '{print , NR}' file1

и - гораздо лучше :-)

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

..выход всегда есть:

a 1
b 2
c 3

ваш путь:

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

сжатым и гибким решением этой проблемы является 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

посмотреть man pr для получения дополнительной информации.


Чисто Баш:

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

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

или после комментария gniourf_gniourf

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