说,我有一个文件foo.txt指定N自变量

arg1

arg2

...

argN

我需要传递给命令my_command

如何使用文件的行作为命令的参数?

"参数"是复数,还是"参数"是单身? 接受的答案仅在单参数的情况下才是正确的(这是正文要查询的内容),而在多参数的情况下则是正确的,主题似乎在此情况下进行查询。

下面的4个答案通常具有相同的结果,但是它们的语义略有不同。 现在我记得为什么我停止编写bash脚本了:P

如果您的shell是bash(以及其他),则$(cat afile)的快捷方式是$(< afile),因此您应编写:

mycommand"$(< file.txt)"

在"命令替换"部分的bash手册页中记录。

或者,从stdin中读取命令,因此:mycommand < file.txt

要学究,不是使用

参数数量是否有限制?如果是这样,我该如何克服呢?

它是内核设置,因此您需要重新编译内核。或研究xargs命令

您能向我解释为什么我在这里需要$吗?

因为bash语法要求使用它,如手册中所述:命令替换

@ykaner,因为如果没有"$(…)",file.txt的内容将被传递给mycommand的标准输入,而不是作为参数。 "$(…)"表示运行命令并以字符串形式返回输出;在这里,"命令"仅读取文件,但是可能更复杂。

除非我失去引号,否则它对我不起作用。用引号将整个文件作为1参数。不带引号,它将每一行解释为一个单独的arg。

如果不带引号,则它将文件中的每个单词都当作一个单独的arg,并且每个单词都容易出现乱码-如果其中一个单词是*,则该单词将扩展为当前目录中的所有文件名。

如前所述,您可以使用反引号或$(cat filename)。

没有提到的,并且我认为需要注意的是,您必须记住,shell会根据空格将文件的内容分开,并将找到的每个"单词"作为参数提供给命令。尽管您可以将命令行参数括在引号中,以便它可以包含空格,转义序列等,但是从文件读取将不会做同样的事情。例如,如果您的文件包含:

a"b c" d

您将得到的参数是:

a

"b

c"

d

如果要把每行作为参数,请使用while / read / do构造:

while read i ; do command_name $i ; done < filename

我应该提到,我假设您正在使用bash。我意识到那里还有其他外壳,但是我使用过的几乎所有* nix机器都运行过bash或类似的程序。 IIRC,此语法在ksh和zsh上应相同。

除非您要扩展反斜杠转义序列,否则Read应当为read -r,并且NUL是比换行符更安全的定界符,特别是如果您传递的参数是文件名之类的东西时,则可以包含文字换行符。同样,如果不清除IFS,则会从i中隐式清除前导和尾随空格。

command `< file`

会将文件内容传递给stdin上的命令,但会删除换行符,这意味着您无法单独遍历每行。为此,您可以编写一个带有" for"循环的脚本:

for i in `cat input_file`; do some_command $i; done

mywiki.wooledge.org/DontReadLinesWithFor

...对于正确的方法,mywiki.wooledge.org / BashFAQ / 001

同样,将< file放在反引号中意味着它实际上根本不执行command的重定向。

(支持此方法的人是否真的对其进行过测试?command `< file`在bash或POSIX sh中均不起作用; zsh是另一回事,但这不是该问题所涉及的shell)。

@CharlesDuffy您能更详细地了解它是如何工作的吗?

@CharlesDuffy这在bash 3.2和zsh 5.3中有效。这在sh,ash和dash中不起作用,但是$(@mwfearnley,很乐意提供这种特异性。目标是遍历行,对吗?将Hello World放在一行上。您会看到它先运行some_command Hello,然后运行some_command World,而不是some_command"Hello World"或some_command Hello World。

您可以使用反引号:

echo World > file.txt

echo Hello `cat file.txt`

这不会创建参数-因为它没有被引号,它受字符串拆分的影响,所以如果在第一步中发出echo"Hello * Starry * World"> file.txt,您将至少将四个单独的参数传递给第二个命令-并且可能更多,因为*将扩展为当前目录中存在的文件名。

...并且由于其正在运行的glob扩展,它不会确切地发出文件中的内容。而且由于它执行命令替换操作,因此效率极低-实际上fork()断开子外壳并在其stdout上附加了FIFO,然后调用bincat作为该子外壳的子代,然后通过FIFO读取输出;与$(相比,后者将文件内容直接读取到bash中,而没有涉及子外壳或FIFO。

如果您想以一种健壮的方式来执行此操作,以使其适用于每个可能的命令行参数(带空格的值,带换行的值,带文字引号字符的值,不可打印的值,带glob字符的值等),它会有点更有趣的。

给定一个参数数组,写入文件:

printf '%s\0'"${arguments[@]}">file

...根据需要替换为"argument one","argument two"等。

要读取该文件并使用其内容(在bash,ksh93或另一个带有数组的最新shell中):

declare -a args=()

while IFS='' read -r -d '' item; do

args+=("$item" )

done

run_your_command"${args[@]}"

要读取该文件并使用其内容(在没有数组的shell中;请注意,这会覆盖您的本地命令行参数列表,因此最好在函数内部完成,这样就覆盖了函数的参数,而不是全球清单):

set --

while IFS='' read -r -d '' item; do

set --"$@""$item"

done

run_your_command"$@"

请注意,-d(允许使用不同的行尾定界符)是非POSIX扩展,并且没有数组的shell也可能不支持它。在这种情况下,您可能需要使用非Shell语言将NUL分隔的内容转换为eval安全形式:

quoted_list() {

## Works with either Python 2.x or 3.x

python -c '

import sys, pipes, shlex

quote = pipes.quote if hasattr(pipes,"quote") else shlex.quote

print("".join([quote(s) for s in sys.stdin.read().split("\0")][:-1]))

'

}

eval"set -- $(quoted_list

run_your_command"$@"

这是我将文件内容作为参数传递给命令的方式:

./foo --bar"$(cat ./bar.txt)"

这是-但实际上效率较低-有效等效于$((当使用带有适当扩展名的shell(例如bash)时)。 $(是一种特殊情况:与$(cat ...)中使用的常规命令替换不同,它不会派生子shell进行操作,因此避免了很多开销。

在我的bash外壳中,以下内容就像一个咒语:

cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'

其中input_file是

arg1

arg2

arg3

显而易见,这使您可以从input_file的每一行执行多个命令,这是我在这里学到的一个不错的小技巧。

从安全角度来看,您的"妙招"很危险-如果您的参数包含$(somecommand),则将使该命令执行而不是作为文本传递。同样,>etcpasswd将作为重定向处理并覆盖etcpasswd(如果以适当的权限运行)等。

相反,(在具有GNU扩展名的系统上)执行以下操作更为安全:xargs -d $

sh -c for arg; do command1"$arg"; command2"arg"; command3"arg"; done _ -而且效率更高,因为它会将尽可能多的参数传递给每个shell,而不是在输入文件中每行开始一个shell。

当然,还要失去对cat的无用使用

Logo

更多推荐