【问题标题】:How to handle 3 files with awk?如何用 awk 处理 3 个文件?
【发布时间】:2014-09-04 13:10:15
【问题描述】:

好的,所以花了 2 天后,我无法解决它,我现在几乎没有时间了。这可能是一个非常愚蠢的问题,所以请多多包涵。我的 awk 脚本执行以下操作:

BEGIN{ n=50; i=n; }
FNR==NR {
            # Read file-1, which has just 1 column
            ids[$1]=int(i++/n);
            next
        }
        {
            # Read file-2 which has 4 columns
            # Do something
            next
        }
 END {...}

它工作正常。但现在我想将其扩展为读取 3 个文件。比方说,我需要读取一个属性文件并从中设置“n”的值,而不是硬编码“n”的值。我找到了this question 并尝试过这样的事情:

BEGIN{ n=0; i=0; }
FNR==NR {
            # Block A
            # Try to read file-0
            next
        }
        {
            # Block B
            # Read file-1, which has just 1 column
            next
        }
        {
            # Block C
            # Read file-2 which has 4 columns
            # Do something
            next
        }
 END {...}

但它不起作用。块 A 为 file-0 执行,我可以从属性文件中读取属性。但是块 B 对文件 file-1 和 file-2 都执行。并且 Block C 永远不会被执行。

有人可以帮我解决这个问题吗?我以前从未使用过 awk,语法非常混乱。此外,如果有人可以解释 awk 如何从不同文件中读取输入,那将非常有帮助。

如果我需要为问题添加更多详细信息,请告诉我。

【问题讨论】:

  • 我很好奇 - 为什么您将 1st 文件称为 file 0 而不是 file 1
  • @EdMorton:我的第一个示例有文件 file-1 和 file-2。在我的下一个例子中,我需要处理一个文件before,因此我使用了file-0,这里没什么特别的。我知道awk 从 1 开始索引文件

标签: awk gawk nawk


【解决方案1】:

如果你有 gawk,只需测试 ARGIND:

awk '
ARGIND == 1 { do file 1 stuff; next }
ARGIND == 2 { do file 2 stuff; next }
' file1 file2

如果你没有 gawk,那就买吧。

在其他 awks 中,您可以只测试文件名:

awk '
FILENAME == ARGV[1] { do file 1 stuff; next }
FILENAME == ARGV[2] { do file 2 stuff; next }
' file1 file2

只有在你想解析同一个文件两次时才会失败,如果是这种情况,你需要添加文件被打开次数的计数。

【讨论】:

  • 感谢您的回答。我目前没有使用gawk,但代码看起来很简单,易于理解和记住,请记住这一点。
【解决方案2】:

更新:以下解决方案有效,只要所有输入文件非空,但请参阅@Ed Morton's answer添加文件特定处理的更简单、更强大的方法

但是,这个答案仍然提供了一些 awk 基础知识以及为什么 OP 的方法不起作用的希望有用的解释。


尝试以下操作(请注意,我已将索引设为基于 1,因为 awk 就是这样做的):

awk '

 # Increment the current-file index, if a new file is being processed.
 FNR == 1 { ++fIndex }

 # Process current line if from 1st file.
 fIndex == 1 {
    print "file 1: " FILENAME
    next
 }

 # Process current line if from 2nd file.
 fIndex == 2 {
    print "file 2: " FILENAME
    next
 }

 # Process current line (from all remaining files).
 {
    print "file " fIndex ": " FILENAME
 }

' file-1 file-2 file-3
  • 模式FNR==1 在开始处理新输入文件时为真(FNR 包含与输入文件相关的行号)。
  • 每次新文件开始处理时,fIndex 都会递增,从而反映当前输入文件的从 1 开始的索引。 @twalberg's helpful answer致敬。

    • 请注意,在数字上下文中使用的未初始化 awk 变量默认为 0,因此无需初始化 fIndex(除非您需要不同的起始值)。
  • fIndex == 1 等模式随后可用于仅对来自特定输入文件的行执行块(假设块以 next 结尾)。
  • 然后对所有没有特定文件块的输入文件执行最后一个块(上图)。

至于为什么你的方法不起作用

  • 对于来自所有输入文件的行,您的第二和第三块可能无条件执行,因为它们前面没有一个模式(条件)。

  • 因此,从 所有 后续输入文件中的行输入您的第二个块,然后其 next 语句防止到达第三个块

潜在的误解

  • 也许您认为每个块都作为一个循环处理单个输入文件。这不是awk 的工作方式。相反,整个 awk 程序在一个循环中处理,每次迭代处理一个输入行,从文件 1 中的所有行开始,然后从文件 2 开始, ...

  • awk 程序可以有任意数量的块(通常以模式开头),它们是否针对当前输入行执行完全取决于模式是否为真;如果没有模式,则无条件地(跨输入文件)执行该块。但是,正如您已经发现的那样,块内的next 可用于跳过后续块(模式块对)。

【讨论】:

  • 按需工作。非常感谢您的回答,更感谢您的解释。 +1
  • @Bhushan:我很高兴听到这个消息;我的荣幸。
  • 当最后一个文件之前的文件为空时,这会失败,我一般不会这样做,因为有更强大的解决方案。此外,在处理多个文件以使其具有多个字段时,这很常见,例如第一个包含值的 2 字段映射,然后下一个包含 N 字段文本,其中需要根据原始文件映射某些字段。
  • 不过,我想说的是,当我们只有 2 个文件(即NR==FNR)时,我们大多数人使用的常见测试在第一个文件为空时也会失败,因此它不会令人发指只是说明这一点!
【解决方案3】:

也许您需要考虑添加一些额外的结构,如下所示:

BEGIN { file_number=1 }
FNR==1 { ++file_number }
file_number==3 && /something_else/ { ...}

【讨论】:

  • +1 表示FNR==1 { ++file_number }(当然,这比尝试检测FILENAME 更改更简单)。但是,由于您正在初始化为1,因此您实际上是从索引2 开始的。在我看来,您可以完全放弃 BEGIN 块。
  • @mklement0 你说得对,在这种情况下 BEGIN 块并不是绝对必要的,但是使用这种结构,用户可以根据需要使用基于 0 的索引(或基于 42 的索引) ,所以我故意把它留给灵活性......
  • 当最后一个文件之前的文件为空时,这会失败,我一般不会这样做,因为有更强大的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-18
  • 2012-08-09
  • 1970-01-01
相关资源
最近更新 更多