假设我有以下 CSV:
$ cat test.csv
id,domain
1,foo.com
2,bar.com
使用mlr put
,我可以轻松地将任何函数映射到 CSV 中的字段,只要我可以在 Miller 中定义它DSL。因此,例如,将为每个记录mlr --csv put '$id = $id + 1'
将 递增1。id
但是,如果我无法在 Miller DSL 中定义该函数(可能是因为它不是纯函数)怎么办?假设我想将 CSV 中的每个域映射到一个 IP 地址。我想做类似的事情mlr --csv put '$ip = shell("nslookup $domain")
。是否有捷径可寻?
目前,我正在将输入字段提取到一个单独的文件中,在单独的 shell 脚本中重写它,然后将结果添加回mlr join
.然而,这相当混乱,因为我的 CSV 充满了引号逗号和换行符,我需要自己小心处理,而不是依赖 Miller。
答案1
更新: 截至 2019 年 9 月, 这
system()
DSL功能可以用于此目的。
从 Miller DSL 调用外部命令
Miller DSL 参考文献中有关调用外部命令的部分重定向输出语句:
这打印,倾倒,球座,发射量,发射, 和发射关键字都允许您将输出重定向到一个或多个文件或管道命令。
我在文档中找不到这一点(除了从示例中推断),但将这些语句与 pipeline-to 命令一起使用的语法似乎是{statement} | {quoted-shell-command}, {unquoted-mlr-expression}
.例如:
$ mlr --csv put 'tee | "tr [a-z] [A-Z]", $*' test.csv
id,domain
1,foo.com
2,bar.com
ID,DOMAIN
1,FOO.COM
2,BAR.COM
请注意,管道输出出现在 Miller 的输出之后(在本例中,是未更改的输入,因为tee
不会影响流并put
发出它)。通过put
使用 抑制 的输出,并使用而不是-q
提取单个字段,我们可以获得 IP 地址列表:print $domain
tee $*
$ mlr --csv put -q 'print | "xargs dig +short", $domain' test.csv
23.23.86.44
104.27.138.186
104.27.139.186
米勒在这里并没有为我们做太多事情。我们仍然必须使用xargs
将标准输入转换为参数(因为dig
不接受标准输入上的域)。此外,dig
的输出包含换行符,这意味着输出不再与输入一对一匹配。由于遵循 Unix 哲学,如果这就是我所需要的,mlr
那么将管道连接到末尾会更容易。mlr --headerless-csv-output cut -f domain
将外部命令的输出连接到您的输入
我真正想做的是将调用外部命令的结果分配给 Miller DSL 中的流内变量,据我所知,这是不可能的。然而,通过xargs
交换GNUparallel
,我们可以使用该--tag
选项来跟踪我们给出的参数dig
,并从灵活的并发 I/O 中受益:
$ mlr --csv --headerless-csv-output cut -f domain test.csv | parallel --tag dig +short
foo.com 23.23.86.44
bar.com 104.27.139.186
bar.com 104.27.138.186
由于我们正在处理 CSV,因此parallel
实际上可以自行处理此问题,尽管我们需要按位置 ( {2}
) 而不是名称 ( domain
) 访问字段:
$ < test.csv parallel -C "," --skip-first-line --tagstring {2} dig +short {2}
foo.com 23.23.86.44
bar.com 104.27.139.186
bar.com 104.27.138.186
这是一个制表符分隔的对列表(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 | \
parallel --skip-first-line --tag dig +short | \
mlr --t2c --implicit-csv-header label domain,ip | \
mlr --c2p --barred join -f test.csv -j domain then \
nest --implode --values --across-records -f ip
+---------+----+-------------------------------+
| domain | id | ip |
+---------+----+-------------------------------+
| foo.com | 1 | 23.23.86.44 |
| bar.com | 2 | 104.27.138.186;104.27.139.186 |
+---------+----+-------------------------------+