如何从 Miller (mlr) 的 DSL 中调用外部命令?

sjy*_*sjy 6 shell csv miller

假设我有以下 CSV:

\n\n
$ cat test.csv\nid,domain\n1,foo.com\n2,bar.com\n
Run Code Online (Sandbox Code Playgroud)\n\n

使用mlr put,我可以轻松地将任何函数映射到 CSV 中的字段,只要我可以在 Miller DSL中定义它。因此,例如,mlr --csv put \'$id = $id + 1\'将增加id1。

\n\n

但是,如果我可以 \xe2\x80\x99t 在 Miller\xe2\x80\x99s DSL 中定义该函数(也许是因为它不是纯函数)怎么办?假设我想将 CSV 中的每个域映射到一个 IP 地址。我\xe2\x80\x99d 喜欢做类似的事情mlr --csv put \'$ip = shell("nslookup $domain")。是否有捷径可寻?

\n\n

目前,我正在将输入字段提取到一个单独的文件中,在单独的 shell 脚本中重写它,然后将结果添加回mlr join. 然而,这相当混乱,因为我的 CSV 充满了引号逗号和换行符,我需要自己小心处理,而不是依赖 Miller。

\n

sjy*_*sjy 5

\n

更新: 自2019年9月起system()DSL功能可用于此目的。

\n
\n

从 Miller DSL 调用外部命令

\n

Miller DSL 参考在重定向输出语句部分中涉及调用外部命令:

\n
\n

print dumpteeemitfemitemip关键字都允许您将输出重定向到一个或多个文件或管道命令

\n
\n

我无法\xe2\x80\x99在文档中找到这个(除了从示例中推断),但使用这些语句与管道命令的语法似乎是{statement} | {quoted-shell-command}, {unquoted-mlr-expression}. 例如:

\n
$ mlr --csv put \'tee | "tr [a-z] [A-Z]", $*\' test.csv\nid,domain\n1,foo.com\n2,bar.com\nID,DOMAIN\n1,FOO.COM\n2,BAR.COM\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,管道输出出现在 Miller\xe2\x80\x99s 输出之后(在这种情况下,是未更改的输入,因为tee不会影响流并put发出它)。通过使用 抑制put\xe2\x80\x99s 输出,并使用而不是-q提取单个字段,我们可以获得 IP 地址列表:print $domaintee $*

\n
$ mlr --csv put -q \'print | "xargs dig +short", $domain\' test.csv\n23.23.86.44\n104.27.138.186\n104.27.139.186\n
Run Code Online (Sandbox Code Playgroud)\n

米勒在这里并没有为我们做太多事情;我们仍然必须使用xargs将标准输入转换为参数(因为dig不接受标准输入上的域)。此外,dig\xe2\x80\x99s 输出包含换行符,这意味着输出不再与输入一对一匹配。由于遵循 Unix 哲学,如果这就是我所需要的,那么mlr将管道连接到末尾会更容易。mlr --headerless-csv-output cut -f domain

\n

将外部命令的输出连接到您的输入

\n

我真正想做的是将调用外部命令的结果分配给 Miller DSL 中的流内变量,据我所知,这是不可能的。然而,通过交换xargsGNU ,我们可以使用该选项parallel--tag来跟踪我们给出的参数dig,并从灵活的并发 I/O 中受益

\n
$ mlr --csv --headerless-csv-output cut -f domain test.csv | parallel --tag dig +short\nfoo.com 23.23.86.44\nbar.com 104.27.139.186\nbar.com 104.27.138.186\n
Run Code Online (Sandbox Code Playgroud)\n

由于我们正在处理 CSV,因此parallel实际上可以自行处理此问题,尽管我们需要按位置 ( {2}) 而不是名称 ( domain) 访问字段:

\n
$ < test.csv parallel -C "," --skip-first-line --tagstring {2} dig +short {2}\nfoo.com 23.23.86.44\nbar.com 104.27.139.186\nbar.com 104.27.138.186\n
Run Code Online (Sandbox Code Playgroud)\n

这是一个制表符分隔的对列表(domain, ip),因此我们可以使用 .txt 将其转换回带有标题的 CSV mlr --t2c --implicit-csv-header label domain,ip。然后,由于我们的输出和原始数据test.csv都有一个domain字段,因此我们可以使用它mlr join来生成单个输出表,并mlr nest内爆 的多个值bar.com

\n
$ mlr --csv cut -f domain test.csv | \\\n  parallel --skip-first-line --tag dig +short | \\\n  mlr --t2c --implicit-csv-header label domain,ip | \\\n  mlr --c2p --barred join -f test.csv -j domain then \\\n  nest --implode --values --across-records -f ip\n+---------+----+-------------------------------+\n| domain  | id | ip                            |\n+---------+----+-------------------------------+\n| foo.com | 1  | 23.23.86.44                   |\n| bar.com | 2  | 104.27.138.186;104.27.139.186 |\n+---------+----+-------------------------------+\n
Run Code Online (Sandbox Code Playgroud)\n