我有一个 CSV 文件,想要对每一行运行一个命令,并使用文件的字段作为单独的参数。
例如给定以下文件:
foo,42,red
bar,13,blue
baz,27,green
我想依次运行以下命令:
my_cmd --arg1 42 --arg2 foo --arg3 red
my_cmd --arg1 13 --arg2 bar --arg3 blue
my_cmd --arg1 27 --arg2 baz --arg3 green
实现此目的的最简单方法是什么?似乎可以使用 xargs 来实现,但我不知道具体如何实现。
GNU
parallel
可以直接读取 csv,并内置了项目替换功能。或多或少直接取自
man parallel
:-j1
在这些调用之前添加my_cmd
一个接一个地执行。或者不添加,让它们并行执行。(在 debian 和 fedora 上,它位于名为 的包中
parallel
,而不是在moreutils
或 中moreutils-parallel
)谢谢你,Ole Tange!
我发现 awk 比 xargs 更容易一些,因此我倾向于使用 awk 组装参数,然后将它们传递给 xargs:
这里
-L1
说的是“每行输入运行一个命令”。下面首先使用 Miller (
mlr
) 将无标头 CSV 输入转换为 JSONL 输出(单个 JSON 对象的行)。jq
然后 JSON 处理器读取这些对象并将其部分作为参数输出到命令。输出是可以eval
-ed 的 shell 代码。输出操作符
@sh
尝试以适合 shell 的方式引用给定的数据。它并非万无一失,但大多数情况下效果不错。您也可以直接从 Miller 运行程序,但我不知道它的
exec()
函数处理需要在 shell 中引用的值的效果如何(或者这是否是个问题)。如果我有时间测试它,我可能会稍后回来修改它。使用Raku(以前称为 Perl_6)
...使用 Raku
Text::CSV
模块:Raku 是 Perl 家族中的一种编程语言,具有一些用于调用外部命令的出色功能。两个选项是 调用
shell
或 调用run
。根据文档,调用run
更安全。上面,当您声明
$parser
对象时,您可以设置各种参数,例如接受非逗号分隔符(例如my $parser = Text::CSV.new(sep => "|");
:)。然后使用逐行读取/解析文件getline()
。上面显示了使用的一个简单输出echo
。示例输入:
示例输出(带有
echo
):下面,使用
run "printf", "%s\t", .[0].uc, .[1], .[2].uc given $_; run "printf", "\n";
,用制表符分隔列输出\t
。请注意,这里我们将.uc
第一列和第三列改为大写,以表明您仍然可以根据需要清理文本(在调用 之前my_cmd
):示例输出(带有
printf
):最后,您可以使用 Raku 的动态变量从命令行中删除输入文件
$*ARGFILES
。显然,您将替换以下my_cmd
内容printf
:否则,请参阅下面的第一个链接,了解如何将输出保存到 Raku“Proc”(进程)对象,或请参阅下面的第二个链接,了解如何使用“Proc::Async”(异步进程接口)。
https://docs.raku.org/type/Proc
https://docs.raku.org/type/Proc/Async
https://raku.org
将字段选择/排序(
f=...
见下文)与添加--argN
(循环)分开,以便可以轻松修改字段和/或排序,并可能使用 anyawk
和 POSIX多次使用相同的字段xargs
:测试完成后删除
echo
。鉴于此,更改顺序和复制字段就像更改一样简单
f='...'
:\"
周围的 s是%s
为了确保xargs
能够正确处理包含空格的字段,否则像这样的字段a b
将被分成 2 个单独的参数my_cmd
。