对于每行输入,将该行与输出一起发送,将其传递给命令

时间:2017-03-09 01:42:42

标签: linux bash

我有一个输入文件,每行有一行,如下所示:

100
200

我打算将每个输入值传递给任意命令(此处为abc),并生成一个输出文件,该输出文件同时包含输入和相关输出。

假设我的命令abc100转换为tada并将200转换为jjhu(实际生产版本将执行不同的操作),我的预期输出是:

100 tada
200 jjhu

目前,我正在尝试使用以下命令:

cat jobs.txt | xargs -n 1 -I {} sh -c "echo {}; abc {}"

...但其输出是:

100
tada
200
jjhu

如何纠正这一点,将输出放在与输入相同的一行?

4 个答案:

答案 0 :(得分:4)

根本不要使用ggplot(binom %>% filter(y > ymin), aes(x, y)) + geom_point(size=1.2, colour="blue") + geom_line(data=normal %>% filter(y > ymin), lwd=0.7, colour="red") + geom_segment(aes(x=x, xend=x, y=0, yend=y), lwd=0.8, alpha=0.5, colour="blue") + facet_grid(. ~ n, scales="free", space="free") 执行此任务。使用xargs(为每行输入运行一个新的独立shell进程)效率很低,并为您提供安全漏洞(考虑您的-n 1100是否有一些转义它只是一个单词)。

以下内容可以在单个shell实例中运行,假设您的输入文件对每行都有一个值:

$(rm -rf $HOME)

答案 1 :(得分:3)

修订问题

对于修订后的问题,您可以执行以下操作之一。一种是使用Charles Duffyanswer

另一个选择是使用paste(这是我在最早的回答中建议的)。假设命令abc足够愚蠢到一次只接受一个参数(这很痛苦;你确定你不能让它升级以在一次调用中处理多个参数吗?),那么你可能会使用方法:

while read -r line
do
    abc "$line"
done < jobs.txt | paste jobs.txt -

如果命令接受多个参数并为每个参数生成一行输出,那么您可以使用:

xargs abc < jobs.txt | paste jobs.txt -

如果该命令为每个参数生成多行输出,那么您需要重新开始:

while read -r line
do echo "$line" $(abc "$line")
done < jobs.txt

因为调用echo之前的参数处理会将abc的输出展平为单行。这里$(…)的外围周围有细致而刻意的引号。如果输出包含可能搞砸shell脚本的字符,那么你必须更加努力 - 它也是可行的(例如echo "$line $(abc "$line" | tr '\n' ' ')")。

这些选项中哪一个最好取决于尚未透露的细节 - 其中任何一个都可能适合您的任务。

问题的先前版本

输入文件似乎是:

100
200

,所需的输出是:

100 abc100
200 abc200

您可以使用sed轻松实现此结果:

sed 's/.*/& abc&/' jobs.txt

用该行,空格,字母abc和行再次替换每行读取的内容。

答案 2 :(得分:3)

如前所述,xargs不适合这项工作:

  • Charles Duffy's helpful answer显示了一个易于扩展的纯shell解决方案,但仅适用于较小的文件(shell循环很慢)。
    如果您需要为每个命令提供shell功能,这是正确的解决方案,就像OP一样,例如在每个输入参数调用 2 命令时。

  • 如果回显值就是你所需要的(结果是不是 OP的用例,但那并不是最初很明显),Jonathan Leffler's helpful answer显示sed命令,对于较大的文件来说,它通常是更快的替代方案。

至于为什么xargs命令无法正常工作

唯一直接的问题是echo使用尾随换行符终止其输出,您可以使用printf移植它。

此外,-n 1是多余的,因为-I {} 并使用每个输入行作为一个整体作为参数,用一个参数替换占位符{}的所有出现(或者,每个POSIX,最多至少5个)。

最后,将{}作为位置参数传递给shell(sh)和双引号通常会更健壮,更安全在shell脚本中的使用。
这可以防止意外修改参数和恶意尝试注入命令。

如果我们把它们放在一起,我们得到:

xargs -I {} sh -c 'printf "%s " "$1"; abc "$1"' - {} < jobs.txt

请注意虚拟参数-,因为第一个参数绑定到shell内的$0,而不是$1

Charles指出,让脚本传递给sh -c处理输入的循环可以完全消除对-I {} / -n 1的需求,并且还可以极大地提高性能,因为(通常)只 sh进程是必需的。

xargs sh -c 'for a; do printf "%s " "$a"; abc "$a"; done' - < jobs.txt

通常,如果输入行有嵌入的空格,并且您仍希望将每一行视为单个参数,则必须使用-d'\n',但请注意,这不适用于BSD / macOS xargs

请注意,这实际上是Charles&#39;的内联变体。自己的答案。

答案 3 :(得分:0)

坚持OP问题的更简单的解决方案是:

cat jobs.txt | xargs -n 1 -I {} sh -c 'echo -n "{} "; abc {}'

正如本页中所解释的,问题是echo命令输出尾随换行符,如果没有指示避免它。 &#34; -n&#34; echo的参数可以做到这一点。

据说xargs对此并不好。可能是,但它确实起了作用:

文件jobs.txt

  

100
  200

文件abc

  回声&#34; $ 1 /&#34;

/tmp$ cat jobs.txt | xargs -n 1 -I {} sh -c 'echo -n "{} "; ./abc {}'
100 a100/
200 a200/
/tmp$

来自下方评论的更新(以及此页面中的其他杂项内容):此代码不安全,易于注入数据。此外,-n 1是多余的,也许还有其他问题。所以,即使这个答案是对OP问题的正确答复:

  

如何纠正这一点,将输出放在与输入相同的一行?

强烈建议不要使用此代码。