system() 产生不一致的结果

Joh*_*ale 7 shell awk date

我正在使用 awk 向 CSV 文件添加一列。文件的每一行都以日期和时间开头。我希望新列是那个时间作为 Unix 纪元时间戳。

我将前三个字段提供给“日期”,这确实给了我时间戳,但有一些我不想要的杂散逗号“,”。下面是一个简化版本,只打印一个时间戳列作为输出。输入文件:

08/17/2020 21:46:04 -700 , 1 , 2 , 3
08/17/2020 21:47:03 -700 , 1 , 2 , 3
08/17/2020 21:48:03 -700 , 1 , 2 
08/17/2020 21:49:04 -700 , 1 , 2 
Run Code Online (Sandbox Code Playgroud)

这是我的程序:

cat input.csv | awk '{
       tmp=$(system("date +%s -d \""$1" "$2" "$3"\""));
       printf("%s", $tmp );
       }'
Run Code Online (Sandbox Code Playgroud)

这是输出。这就是我所期望的,除了第 2 行和第 3 行的前导逗号。我认为 'tmp' 变量从 'date' 获得结果,包括一个尾随换行符,但有时在换行符后还有一个逗号。这似乎取决于输入文件中有多少字段跟随前三个,但我只引用前三个,如 $1 $2 $3 所以输入行的其余部分不起作用(?)为什么那些逗号出现在输出,我怎么能删除它们?有一个更好的方法吗?

1597725964
,1597726023
,1597726083
1597726144
Run Code Online (Sandbox Code Playgroud)

Joh*_*024 8

使用外壳

尝试:

$ while read -r line; do date +%s -d "${line%%,*}"; done < input.csv
1597725964
1597726023
1597726083
1597726144
Run Code Online (Sandbox Code Playgroud)

这个怎么运作

  1. while read -r line; do开始一个while循环并从标准输入读取一行。

  2. "${line%%,*}" 从行中删除逗号及其后的所有内容。

  3. date +%s -d "${line%%,*}" 将日期打印为纪元。

  4. done完成while循环。

  5. <input.csv 为循环提供标准输入。

变化

这将打印整行并将纪元添加为最后一列:

$ while read line; do printf "%s, %s\n" "$line" $(date +%s -d "${line%%,*}"); done < input.csv
08/17/2020 21:46:04 -700 , 1 , 2 , 3, 1597725964
08/17/2020 21:47:03 -700 , 1 , 2 , 3, 1597726023
08/17/2020 21:48:03 -700 , 1 , 2, 1597726083
08/17/2020 21:49:04 -700 , 1 , 2, 1597726144
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你们俩。John1024 你的“变体”代码正是我想要的。我希望它对其他人有用,尽管可能不会在搜索中找到,因为我的问题标题是基于我对 awk system() 工作原理的误解。 (2认同)

hek*_*mgl 6

awk你可以使用 getline 而不是 system() 的协进程:

< input.csv awk -F' , ' '{
    "date +%s -d \047"$1"\047\n" | getline date
    print date
}'
1597725964
1597726023
1597726083
1597726144
Run Code Online (Sandbox Code Playgroud)

的帮助下Inian奥古兹·伊斯梅尔的意见,并且gawk,我们想出了一个更好的解决方案,其中写入最新的标准输入,而不是通过命令行传递参数给它。这更好,因为将变量插入命令行总是伴随着 shell 命令注入的风险(通过 input.csv)。

< input.csv gawk -F' , ' '{
    cmd = "date +%s -f-";
    print $1 |& cmd;
    close(cmd, "to");
    if ((cmd |& getline line) > 0)
        print line; close(cmd)
}'
1597725964
1597726023
1597726083
1597726144
Run Code Online (Sandbox Code Playgroud)

感谢两位!


ogu*_*ail 5

调用system(...)返回零,因此tmp被赋值$(0),即整个输入行。观察:

$ echo a b c d | awk '{ x = $(system("exit 3")); print x }'
c
Run Code Online (Sandbox Code Playgroud)

无法使用systemawk 中的函数捕获 shell 命令的输出;hek2mgl 的回答演示了如何正确地做到这一点。

然后在printf(...)调用$tmp中扩展为$8,因为$0构成有效数字的最长前缀是08; 因此输出中的逗号。可以这样证明:

$ echo foo bar | awk '{ x = "0002junk"; print $x }'
bar
Run Code Online (Sandbox Code Playgroud)

无论如何,为了完成问题中描述的任务,您实际上并不需要 awk。cut和 GNU的结合date产生所需的输出。

$ cut -d, -f1 input.csv | date -f- +%s
1597725964
1597726023
1597726083
1597726144
Run Code Online (Sandbox Code Playgroud)

并且使用paste,如果您不介意逗号周围缺少空格,您可以将这些时间戳附加到相应的记录。

$ cut -d, -f1 input.csv | date -f- +%s | paste -d, input.csv -
08/17/2020 21:46:04 -700 , 1 , 2 , 3,1597725964
08/17/2020 21:47:03 -700 , 1 , 2 , 3,1597726023
08/17/2020 21:48:03 -700 , 1 , 2,1597726083
08/17/2020 21:49:04 -700 , 1 , 2,1597726144
Run Code Online (Sandbox Code Playgroud)


Rav*_*h13 5

您能否尝试在 GNU 中遵循、编写和测试awk。您可以在使用它的同时使用它自己的mktime功能,awk您不需要使用外部命令awk本身可以照顾它。

awk '
{
  split($2,array,":")
  print mktime(substr($0,7,4)" "substr($0,1,2)" "substr($0,4,2) OFS array[1] OFS array[2] OFS array[3])
}' Input_file
Run Code Online (Sandbox Code Playgroud)