【问题标题】:Invoking 'date' command inside awk string, with +%a formatting在 awk 字符串中调用“日期”命令,格式为 +%a
【发布时间】:2015-05-26 00:31:09
【问题描述】:

这个网站还是新手,但这里有...... 基本上我将事件存储在多个文件中,每个事件都是一行,每一行包含日期($1)、开始($2)和停止($3)时间以及其他几条数据。我使用两个双下划线(“__”)作为字段分隔符。我一直在使用各种 shell 脚本来管理数据,并且我正在使用 awk 来计算统计数据,但我在调用 date 函数时遇到了麻烦,因此我可以按星期几获得总数。经过大量修补和扫描讨论板后,我得到了这个:

ls /home/specified/folder/MBRS.db/* |
xargs -n 1 -I % awk -F"__" '$6 == "CLOSED" && $1 >= "'$backDATE'" { print $0 }' % |
awk 'BEGIN{FS="__"}{specDATE=system("date --date="$1" +%a")} specDATE == "Tue" {print $2" "$3}'

ls /home/lingotech/Einstein/data/MBRS.db/* |
xargs -n 1 -I % awk -F"__" '$6 == "CLOSED" && $1 >= "'$backDATE'" { print $0 }' % |
awk 'BEGIN{FS="__"}system("date --date="$1" +%a") == "Mon" {print $2" "$3}'`

我没有输出开始和停止时间,而是为每个条目获取一周中所有不同日期的列表。

我尝试了比我愿意承认的更多的日期用法变体,包括:

for y in Sun Mon Tue Wed Thu Fri Sat; do
  for directory in $( ls /home/specified/directory/MBRS.db/* | xargs -n 1 ); do
    printf "."
    [[ $( cat $directory | awk -F"__" '$6 == "CLOSED" && $1 >= "'$backDATE'" { print $1 }' | xargs -n 1 -I z date +%a -d z ) == "$y" ]] && echo BLAH
  done
done

非常感谢对我搞砸的事情进行一些有用的解释。提前致谢。哦,我以 YYMMDD 格式存储日期,但这对于 ubuntu 服务器的“日期”版本似乎不是问题。

【问题讨论】:

  • 我认为您需要像这样转义{specDATE=system("date --date="$1" +%a")} 中的嵌套双引号:{specDATE=system("date --date=\"$1\" +%a")}。至少,我会对那段语法持怀疑态度,因为当 shell 看到 $1 时,它在引号之外。我认为您应该使用除awk 之外的可以更直接地管理日期的语言进行编码。 awk 很棒——不要误会我的意思。但是每行数据运行一次date 对性能不利。我会使用 Perl(但我已经使用 Perl 二十年了);对你来说使用 Python 可能更明智。
  • 我认为xargs 命令中的-n 1-I %(以及后续的%)只是减慢处理速度的方法; awk 愉快地处理多个文件。在两个awk 脚本中将字段分隔符设置为__ 的不对称方式很奇怪。第二个脚本应该与第一个脚本结合使用,因此您只运行一个awk 脚本。允许将awk 脚本放在多行上。将每个pattern { action } 单元放在自己的线上通常是明智的,除非所有东西都可以舒适地放在一条线上。如果{action} 本身需要多行,请使用它们。
  • 我最初是从一个 awk prog 开始的,但由于遇到问题,我使用管道和 xargs 尝试分离出我正在做的事情以查看错误。例如,当我使用以下内容时,我可以打印出星期几,但是我无法将其用于任何类型的条件检查(即这是星期二): ls /home/absolute/path/* | xargs -n 1 -I % awk -F"" '$6 == "CLOSED" && $1 >= "'$backDATE'" { print $0 }' % | awk 'BEGIN{FS=""} $4 == "SJ" {system("date -d"$1" +%a")}'
  • awk -F "__" -v newDAY="date -d "$1" +%a" '$6 == "CLOSED" && $1 >= "'150000'" {print newDAY}' /home/lingotech/Einstein/data/MBRS.db/* ....类似这样的东西例如会将 newDAY 作为字符串传递 "date -d "$1" +%a" 但我希望它运行日期命令并存储其值
  • 实际上,如果您使用的是 Ubuntu,gawkstrftime 可以简化事情。当前的mawk(相对于Ubuntu中的版本)也支持strftime

标签: bash shell date awk


【解决方案1】:

好的,所以我最终使用了这个:

>backDATE=150000; 
>     for x in $listOFsites; do
>        for y in Sun Mon Tue Wed Thu Fri Sat; do
>            totalHOURS=$( awk 'BEGIN{FS="__"} NF == 10 && $1 >= "'$backDATE'" && $4 == "'$x'" && $6 == "CLOSED" {while ( ( "date +%a -d \""$1"\"" | getline newDAY) > 0 ){if (newDAY == "'$y'") print $2" "$3}}' /home/absolute/path/* | xargs -I % /home/custom/duration/calc % | paste -sd+ | bc ); printf "."; 
>        done
>     done

我必须在单引号内使用日期(这样我就可以将 $1 传递给它)而不是在外面(使用 -F"__" -v newDAY=...),但在单引号内得到输出system() 是有问题的。在看到:How can I pass variables from awk to a shell command? 之后,我终于看到了 while (cmd | get line x) 格式,这是我的问题的症结所在。给 Ed Morton 的道具

【讨论】:

  • 别怪我 :-) - 这不是写该命令的方式!
  • 在日期段中添加了 \" 并调整了 while 语句,但它的执行方式仍然相同。如果你能稍微解释一下“为什么”,它真的会帮助我理解我在做什么错了。只是使用发布的代码有点让我在这里:)
【解决方案2】:

我不知道其余的所有内容(对于我的阅读口味而言,文字太多了!)但是根据您发布的答案,它的这一部分:

awk 'BEGIN{FS="__"} NF == 10 && $1 >= "'$backDATE'" && $4 == "'$x'" && $6 == "CLOSED" {while ( "date +%a -d "$1"" | getline newDAY){if (newDAY == "'$y'") print $2" "$3}}' /home/absolute/path/*

假设它做你想做的事情会写成:

awk -v backDATE="$backDATE" -v x="$x" -v y="$y" '
    BEGIN { FS="__" }
    (NF == 10) && ($1 >= backDATE) && ($4 == x) && ($6 == "CLOSED") {
        cmd = "date +%a -d \"" $1 "\""
        while ( (cmd | getline newDAY) > 0 ) {
            if (newDAY == y) {
                print $2, $3
            }
        }
        close(cmd)
    }
' /home/absolute/path/*

为什么要使用 awk 变量而不是让 shell 变量扩展成为 shell 脚本主体的一部分,答案是健壮性和简单性。

这是让 shell 变量扩展成为 awk 脚本主体的一部分:

$ x="hello world"
$ awk 'BEGIN{ print '$x' }'
awk: cmd. line:1: BEGIN{ print hello
awk: cmd. line:1:                   ^ unexpected newline or end of string
$ awk 'BEGIN{ print "'$x'" }'
awk: cmd. line:1: BEGIN{ print "hello
awk: cmd. line:1:              ^ unterminated string
awk: cmd. line:1: BEGIN{ print "hello
awk: cmd. line:1:              ^ syntax error
$ awk 'BEGIN{ print "'"$x"'" }'
hello world
$ x="hello
world"
$ awk 'BEGIN{ print "'"$x"'" }'
awk: cmd. line:1: BEGIN{ print "hello
awk: cmd. line:1:              ^ unterminated string
awk: cmd. line:1: BEGIN{ print "hello
awk: cmd. line:1:              ^ syntax error

这是使用一个用 shell 变量的值初始化的 awk 变量:

$ x="hello world"
$ awk -v x="$x" 'BEGIN{ print x }'
hello world

$ x="hello
world"
$ awk -v x="$x" 'BEGIN{ print x }'
hello
world

看到区别了吗?

至于为什么将命令存储在变量中 - 因为您必须在使用后关闭它,并且在关闭命令中的拼写方式必须与打开管道时完全相同。比较:

cmd = "date +%a -d \"" $1 "\""
cmd | getline
close(cmd)

对比:

"date +%a -d \"" $1 "\"" | getline
close("date +%a -d \"" $l "\"")

并用非常接近的时间l确定第二版中的错误。

【讨论】:

  • 我在 awk 程序之外设置了 backDATE、x 和 y(并在其他地方使用它们),那么使用 -v 而不是使用 "'$x'" 来扩展多变的?还想知道关于使用 cmd 变量而不是仅仅在条件语句中写出命令的同样的事情吗?不想特别,但我只是想弄清楚一种方法是否更有效地让 awk 阅读,或者它是否对人类更容易等等......
  • 我刚刚编辑了我的答案,以添加解释为什么要使用变量以及为什么要使用 cmd 变量。
  • 谢谢你把事情搞清楚了。使用 -v 来制作变量的 awk 特定版本肯定会为我节省无数时间来寻找流氓引号,感谢您跟进该解释。另外,我之前完全错过了 close(),再次感谢!
猜你喜欢
  • 1970-01-01
  • 2016-12-06
  • 2014-05-06
  • 2014-04-23
  • 1970-01-01
  • 1970-01-01
  • 2016-06-12
  • 2013-11-24
  • 1970-01-01
相关资源
最近更新 更多