【问题标题】:Fill blank lines in a text file with each line of another file用另一个文件的每一行填充文本文件中的空白行
【发布时间】:2020-06-23 13:49:42
【问题描述】:

所以基本上我有一个包含很多空行的文本文件。我们称它为 time.txt,该文件的一部分如下所示:

       1          5   20    
       2          5   12    
       1          6   3    
       2          6   4

       1          10   30    
       2          10   21    
       1          11   27    
       2          12   8
       1          11   34    
       2          12   20

       1          10   30    
       2          10   21  

现在,我有另一个文件,名为 location.txt,其中包含的行数与 time.txt 中的空白行数相同。它看起来像这样:

   110      -7      5.000              66
   110      -7      5.000              99
   110      -7      5.000              60

而我想要的其实很简单:我只想用 location.txt 中的每一行填充 time.txt 中的空白行,给出预期的结果:

   110      -7      5.000              66
       1          5   20    
       2          5   12    
       1          6   3    
       2          6   4
   110      -7      5.000              99
       1          10   30    
       2          10   21    
       1          11   27    
       2          12   8
       1          11   34    
       2          12   20
   110      -7      5.000              60
       1          10   30    
       2          10   21  

我解决这个问题的方法是逐行读取 location.txt,将每一行存储在循环内的一个变量中,然后使用 awk 检测 time.txt 中的空行 并将其替换为存储的循环变量。我的代码如下所示:

time="time.txt"
location="location.txt"
while read -r lines_locs; do
    awk '!NF{$0=$lines_locs}1' $time
done < "$location"

但这只会在我的屏幕上打印出 time.txt,而不会进行任何替换。另外,与预期的行数相比,我打印的行数过多。我确定我遗漏了一些东西,如果有人能指出这一点,我会很高兴。

【问题讨论】:

  • 您希望每个文件有多少行?另外,空白行真的是空白还是它们包含空格(例如,空格、制表符)?
  • time.txt 中的 39757 行和 location.txt 中的 643 行

标签: bash awk while-loop


【解决方案1】:

在 awk 中使用getline

$ awk -v file="$location" 'NF==0{if((getline < file)<=0)$0=""}1' "$time"

解释:

$ awk -v file="$location" '    # location file as parameter
NF==0 {                        # NF==0 considers bare space records empty
    if((getline < file)<=0)    # when empty read from another file. if failure
        $0=""                  # reset the record. see comments for discussion
}1' "$time"                    # output

输出:

   110      -7      5.000              66
       1          5   20    
       2          5   12    
       1          6   3    
       2          6   4
   110      -7      5.000              99
       1          10   30    
       2          10   21    
       1          11   27    
       2          12   8
       1          11   34    
       2          12   20
   110      -7      5.000              60
       1          10   30    
       2          10   21  

如果文件location 的记录用完,脚本会打印空记录。相关讨论见 cmets。

【讨论】:

  • 这非常快速和高效,谢谢!
  • 请注意 getline 问题,特别是如果您不打算在使用它的输出之前测试它是否成功 - 请参阅awk.freeshell.org/AllAboutGetline。您可以通过测试NF 而不是$0=="" btw 来提高效率。
  • @EdMorton 感谢NF 的提示。关于getline 问题:在此应用程序中是否有任何特定问题让您想到?我假设 $0 设置为 "" 以防读取错误,而 if((getline &lt; file) &lt;= 0){print "";next} 不会更安全?
  • 最明显的问题是 location.txt 的行数没有 time.txt 中的空行多,或者 location.txt 在 awk 执行时变得不可读。当 getline 失败时,$0 将保持调用 getline 之前的状态(所以是的,在这种情况下是 ""),并且防止它的方法更像是 if ( (getline &lt; file) &lt;=0 ) print "the sky is falling" | "cat&gt;&amp;2" 或类似的,因此用户会收到有关它的警告。如果 location.txt 中没有足够的行,我发布的非 getline 版本也不会报告 - 作为练习:-)
  • 当前示例不适合演示这一点,因为它为空白输入行调用 getline 并在 getline 失败时打印空白行,但是当您尝试以这种方式使用 getline 时会出现一个常见问题使用命令输出更新输入文件,但其中一个输入文件(例如 file1)无法打开。如果您使用awk 'script' file1 file2 &gt; tmp &amp;&amp; mv tmp file2,则无法打开file1 意味着我们不会覆盖另一个输入文件file2。相反,如果我们使用 awk '...getline &lt; file...' file2 &gt; tmp &amp;&amp; mv tmp file2 unprotected 那么 file2 将被覆盖
【解决方案2】:

如果你对GNU sed没问题,你可以使用

sed -e '/^$/{R '"$location" -e 'd}' "$time"
  • /^$/ 匹配 $time 文件中的空行
  • R 命令允许您从给定文件中一次读取一行,在这种情况下为$location
  • d 命令然后删除空行
  • 如果空行匹配但没有更多行可读取,则空行被删除

如果您想就地修改$time 文件,请使用sed -i

【讨论】:

    【解决方案3】:

    假设location.txt 没有大到无法放入内存:

    $ awk 'NR==FNR{loc[NR]=$0; next} {print (NF ? $0 : loc[++c])}' location.txt time.txt
       110      -7      5.000              66
           1          5   20
           2          5   12
           1          6   3
           2          6   4
       110      -7      5.000              99
           1          10   30
           2          10   21
           1          11   27
           2          12   8
           1          11   34
           2          12   20
       110      -7      5.000              60
           1          10   30
           2          10   21
    

    【讨论】:

      猜你喜欢
      • 2022-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多