我有一个输入文件,每行有一行,如下所示:
100
200
我打算将每个输入值传递给任意命令(此处为abc
),并生成一个输出文件,该输出文件同时包含输入和相关输出。
假设我的命令abc
将100
转换为tada
并将200
转换为jjhu
(实际生产版本将执行不同的操作),我的预期输出是:
100 tada
200 jjhu
目前,我正在尝试使用以下命令:
cat jobs.txt | xargs -n 1 -I {} sh -c "echo {}; abc {}"
...但其输出是:
100
tada
200
jjhu
如何纠正这一点,将输出放在与输入相同的一行?
答案 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 1
是100
是否有一些转义它只是一个单词)。
以下内容可以在单个shell实例中运行,假设您的输入文件对每行都有一个值:
$(rm -rf $HOME)
答案 1 :(得分:3)
对于修订后的问题,您可以执行以下操作之一。一种是使用Charles Duffy的answer。
另一个选择是使用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问题的正确答复:
如何纠正这一点,将输出放在与输入相同的一行?
强烈建议不要使用此代码。