【问题标题】:How to read content from two files and merge into a 3rd file in bash shell如何从两个文件中读取内容并合并到 bash shell 中的第三个文件中
【发布时间】:2013-06-19 01:21:46
【问题描述】:

如何在 bash 中同步读取/处理 2 个文件?

我有 2 个文本文件,其中的行数/项目数相同。 一个文件是

a
b
c

另一个文件是

1
2
3

如何同步循环遍历这些文件,以使a1、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

【问题讨论】:

  • 要使用文件的内容填充数组,我会将$IFS 设置为\n 并使用命令array=($(<file)),然后使用命令for ((n=0;n<${#array[@]};n++));do echo "${array[n]}";done 来处理数组(s )。

标签: regex bash awk sed pattern-matching


【解决方案1】:

您可以使用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

输出:

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

【讨论】:

  • 这绝对是最好的答案! +1.
【解决方案2】:

使用paste (invocation) 合并文件,然后一次处理一行合并文件:

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

【讨论】:

    【解决方案3】:

    GNU 代码

    • 前面有file1

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

    • 前面有file2

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

    两者都导致相同的输出:

    一个 1 b 2 3 d 4 5 f 6 克 7

    【讨论】:

    • 希望输入中没有斜线。 :-) 不过看看paste file1 file2
    • @chirlu 你是对的,如果斜线必须改变正则表达式(稍微):-)
    【解决方案4】:

    的两个例子:

    awk '{print $0, NR}' file1
    

    而且 - 好多了:-)

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

    ..输出总是:

    a 1
    b 2
    c 3
    

    【讨论】:

      【解决方案5】:

      你的方式:

      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
      

      【讨论】:

        【解决方案6】:

        这个问题的一个简洁灵活的解决方案是 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 了解更多信息。

        【讨论】:

          【解决方案7】:

          纯猛击:

          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
          

          【讨论】:

          • 不幸的是,数组中的字段不会包含文件中的行,而是单词!你最好使用mapfile 这样:mapfile -t hostnames &lt; hostnames.txt 而且效率更高!
          • time(1) 在使用 30000 行文件时没有显着差异。开关 -t 删除尾随换行符。对我来说,两个版本的数组内容是相等的。我忽略了什么吗?
          • 它在我的系统上运行,它可能比您的系统更旧且速度更慢。此外,$(...) 运行一个子shell,而mapfile 没有。此外,a=( $( &lt; file ) ) 将创建一个数组,其字段是文件的单词,而不是文件的行。
          • 谢谢。单词问题可以通过设置 IFS 来解决。选择mapfile可以留给读者。
          猜你喜欢
          • 2012-09-26
          • 2020-03-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-09-06
          • 1970-01-01
          • 1970-01-01
          • 2016-10-13
          相关资源
          最近更新 更多