如何将文本文件的每一行作为参数传递给命令?


42

我正在寻找一个脚本,该脚本以.txt文件名作为参数,逐行读取文件,并将每一行传递给命令。例如,先运行command --option "LINE 1",然后运行,command --option "LINE 2"等等。命令的输出将写入另一个文件。我该怎么做?我不知道从哪里开始。

Answers:


26

使用while read循环:

: > another_file  ## Truncate file.

while IFS= read -r LINE; do
    command --option "$LINE" >> another_file
done < file

另一个是按块重定向输出:

while IFS= read -r LINE; do
    command --option "$LINE"
done < file > another_file

最后是打开文件:

exec 4> another_file

while IFS= read -r LINE; do
    command --option "$LINE" >&4
    echo xyz  ## Another optional command that sends output to stdout.
done < file

如果其中一个命令读取输入,这将是使用另一种FD输入这样的命令不会吃一个好主意(这里假设kshzshbash-u 3,使用<&3替代便携):

while IFS= read -ru 3 LINE; do
    ...
done 3< file

最后接受参数,您可以执行以下操作:

#!/bin/bash

FILE=$1
ANOTHER_FILE=$2

exec 4> "$ANOTHER_FILE"

while IFS= read -ru 3 LINE; do
    command --option "$LINE" >&4
done 3< "$FILE"

哪个可以运行为:

bash script.sh file another_file

额外的想法。与一起bash使用readarray

readarray -t LINES < "$FILE"

for LINE in "${LINES[@]}"; do
    ...
done

注意:IFS=如果您不介意将行值修剪为前导空格和尾随空格,可以将其省略。


28

另一个选择是xargs

使用GNU xargs

< file xargs -I{} -d'\n' command --option {} other args

{} 是文本行的占位符。

其他的xargs则没有-d,但是有些具有-0用于NUL分隔的输入。有了这些,您可以执行以下操作:

< file tr '\n' '\0' | xargs -0 -I{} command --option {} other args

在符合Unix的系统上(-I在POSIX中是可选的,仅对于符合Unix的系统是必需的),您需要预处理输入以用以下期望的格式引用xargs

< file sed 's/"/"\\""/g;s/.*/"&"/' |
  xargs -E '' -I{} command --option {} other args

但是请注意,某些xargs实现对参数的最大大小有非常低的限制(例如,在Solaris上为255,是Unix规范允许的最小值)。


还原为<file xargs -L 1 -I{} command --option {} other args
iruvar,

@iruvar,不,它将处理引号并删除一些空格。
斯特凡Chazelas

7

精确地回答这个问题:

#!/bin/bash

# xargs -n param sets how many lines from the input to send to the command

# Call command once per line
[[ -f $1 ]] && cat $1 | xargs -n1 command --option

# Call command with 2 lines as args, such as an openvpn password file
# [[ -f $1 ]] && cat $1 | xargs -n2 command --option

# Call command with all lines as args
# [[ -f $1 ]] && cat $1 | xargs command --option

1
像魅力一样工作:-)
BugHunter

3

我发现的最佳答案是:

for i in `cat`; do "$cmd" "$i"; done < $file

编辑:

...四年后...

经过几次否决和更多的经验后,我现在建议以下

xargs -l COMMAND < file

3
这将为文件中的每个单词调用一次命令。另外,do "$cmd" "$i";除非有理由不这么做,否则应始终引用对shell变量的引用(如中所述)。如果文件*本身包含一个单词,那么您的代码将运行$cmd *,该代码当然将运行命令并显示当前目录中的文件列表。
G-Man说“恢复莫妮卡”

1
@ G-Man,除了中的zsh`cat`将已经扩展了*(如果引号$i的扩展`cat`引入了通配符,则第二引号仍可以扩展未引号)。无论如何,这种方法确实是错误的。
斯特凡Chazelas

1
+1。如果每行仅包含正在使用的命令所需的输入而没有空格,则此方法将正常工作。
Subin Sebastian

这作用于每个单词,是否有变体作用于每一行?
thebunnyrules

问题是逐行处理文件名列表。
Steffen Roller

2
    sed "s/'/'\\\\''/g;s/.*/\$* '&'/" <<\FILE |\
    sh -s -- command echo --option
all of the{&}se li$n\es 'are safely shell
quoted and handed to command as its last argument
following --option, and, here, before that echo
FILE

输出值

--option all of the{&}se li$n\es 'are safely shell
--option quoted and handed to command as its last argument
--option following --option, and, here, before that echo

-2
ed file.txt
%g/^/s// /
2,$g/^/-,.j
1s/^/command/
wq
chmod 755 file.txt
./file.txt

将文件的所有行作为参数传递给单个命令,即

command line1 line2 line3 ....

如果您需要--option在每行之前放置标志,请将第二个命令更改为:

%g/^/s// --option /

1
实际使用ed ...的-1是多少?
user2217522 2014年

3
我没有投票给你,但是做这件事的人可能有以下原因:(1)这并不能满足您的要求。这会在命令行上一次调用命令,并带有文件的所有内容。而不是每行一次。(2)这对处理shell特有的字符(可能在文件中)没有任何作用;例如'"<>;,等。(3)这产生了不必要的临时文件。(4)此类事情通常是通过“此处文档”完成的。(5)您的ed命令很笨拙;前两个命令可以简化为%s/^/ /%j
G-Man说“恢复莫妮卡”
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.