gawk/awk:将日期通过管道传递给getline有时不起作用。

7
我正在尝试将日期从一种格式转换为另一种格式: 例如,从“2005年10月29日”到“2005-10-29”。 我有一个包含625个日期的列表。我使用Awk。
转换通常有效,但有时转换根本不会发生, 并且变量(转换后的日期)保持未定义状态。
这总是发生在完全相同的行上。 在那些奇怪行的日期上显式运行“date”(从Bash shell)可以正常工作(日期被正确转换)。 -- 这与那些行的文本内容无关。
为什么会出现这种情况,我该如何修复我的脚本?
这是我的脚本:
awk 'BEGIN { FS = "unused" } { 
  x = "undefined";
  "date \"+%Y-%m-%d\" -d " $1 | getline x ;
  print $1 " = " x
}' uBXr0r15.txt \
 > bug-out-3.txt

如果您想重现这个问题:
1.下载此文件:uBXr0r15.txt
2.运行Awk脚本。
3.在bug-out-3.txt中搜索“undefined”。(在我的电脑上找到了122次“undefined”)
然后,您可以再次运行脚本,而在我的电脑上,bug-out-3.txt保持不变——完全相同的日期仍然未定义。
(Gawk版本3.1.6,Ubuntu 9.10。)
此致,敬礼,Magnus
3个回答

11
每当你在awk中打开一个管道或文件进行读写时,后者会首先检查(使用内部哈希表)是否已经有具有相同名称的管道或文件(仍然)处于打开状态;如果是,则它将重用现有的文件描述符而不是重新打开管道或文件。
在您的情况下,所有以undefined结束的条目实际上都是重复的;第一次遇到它们时(即第一次发出相应的命令date "..." -d "..."时),正确的结果被读入x。在同一日期的后续出现中,getline尝试从原始date管道中读取第二、第三等行,即使该管道已被date关闭,也导致x不再被分配。
来自gawk手册页:
注意:如果使用管道、协进程或套接字对getline进行读取,或在循环内使用print或printf,必须使用close()创建新的命令或套接字实例。AWK在管道、套接字或协进程返回EOF时不会自动关闭它们。
每次读取x后,您应明确地close管道。
close("date \"+%Y-%m-%d\" -d " $1)

顺便问一下,在使用管道传递到 awk 之前,sortuniq uBXr0r15.txt 可以吗?还是你需要保留原来的顺序/重复性?


这解决了我的问题,谢谢。我不需要原始排序,如果我重新排列我的输入数据,问题也会消失 - 我想我还可以节省一些 CPU。 (真实世界的输入数据还包含非日期行,因此我不能使用 sort' 和 uniq',而且真实的 Awk 脚本有所不同。) - KajMagnus
谢谢,这也解决了我的问题 - 我一直在收到“打开的文件太多”的错误提示,不知道如何关闭这些“文件”,因为我不知道awk在这些管道操作中使用文件。 - cbix
我注意到在调用重新播种随机数的函数时,出现了这个问题。 - user4401178

3
 gawk 'BEGIN{
       m=split("January|February|March|April|May|June|July|August|September|October|November|December",d,"|")
       for(o=1;o<=m;o++){
          months[d[o]]=sprintf("%02d",o)
       }
       FS="[, ]"
    }
    {
      gsub(/["]/,"",$1)
      gsub(/["]/,"",$4)
      t=mktime($4" "months[$1]" "$2" 0 0 0")
      print strftime("%Y-%m-%d",t)
    }' uBXr0r15.txt

在gawk内部执行所有操作比调用外部命令更快。


这是一种雄心勃勃的解决方案 :-) 运行良好。虽然其他解决方案需要更长时间的几分之一秒,但对我来说等待那么长时间还是可以的 :-) - KajMagnus

3

虽然我喜欢awk,但这并不是必要的。

tr -d '"' < uBXr0r15.txt | date +%Y-%m-%d -f -


谢谢,我不知道我可以这样做。我原帖中的示例是一个简化的示例。我的真实 Awk 脚本有点长,而且真实的输入文件也包含非日期行。 - KajMagnus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接