假设我有以下 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\'
将增加id
1。
但是,如果我可以 \xe2\x80\x99t 在 Miller\xe2\x80\x99s DSL 中定义该函数(也许是因为它不是纯函数)怎么办?假设我想将 CSV 中的每个域映射到一个 IP 地址。我\xe2\x80\x99d 喜欢做类似的事情mlr --csv put \'$ip = shell("nslookup $domain")
。是否有捷径可寻?
目前,我正在将输入字段提取到一个单独的文件中,在单独的 shell 脚本中重写它,然后将结果添加回mlr join
. 然而,这相当混乱,因为我的 CSV 充满了引号逗号和换行符,我需要自己小心处理,而不是依赖 Miller。
\n\n更新: 自2019年9月起,
\nsystem()
DSL功能可用于此目的。
Miller DSL 参考在重定向输出语句部分中涉及调用外部命令:
\n\n\nprint 、dump、tee、emitf、emit和emip关键字都允许您将输出重定向到一个或多个文件或管道命令。
\n
我无法\xe2\x80\x99在文档中找到这个(除了从示例中推断),但使用这些语句与管道命令的语法似乎是{statement} | {quoted-shell-command}, {unquoted-mlr-expression}
. 例如:
$ 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 $domain
tee $*
$ 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
我真正想做的是将调用外部命令的结果分配给 Miller DSL 中的流内变量,据我所知,这是不可能的。然而,通过交换xargs
GNU ,我们可以使用该选项parallel
--tag
来跟踪我们给出的参数dig
,并从灵活的并发 I/O 中受益
$ 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
) 访问字段:
$ < 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
:
$ 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