如何从文件中传递命令行参数

6

我有一个读取命令行参数的C程序,它从argv中读取参数。是否可以创建一个管道将文件的内容重定向为我的程序的命令行参数?假设我有一个名为arguments.dat的文件,其内容如下:

0 0.2 302 0

我希望我的程序能够被以下方式调用:

./myprogram 0 0.2 302 0

我尝试了以下内容:
cat arguments.dat | ./myprogram

没有成功。

3个回答

17

xargs 是你的答案:

cat arguments.dat | xargs ./myprogram

或者更简单的方法:

xargs -a arguments.dat ./myprogram

查看手册以了解自定义xargs的多种方法。例如,您可以按行而不是按单词逐行阅读,并且可以在更复杂的替换中使用参数。


这里有一些需要注意的地方——如果您的文件包含的参数超过了单个命令行所能容纳的数量(请记住,环境变量和命令行参数使用相同的空间——因此导出太多或太大的命令行变量会减少最大命令行长度),xargs将会在多次运行./myprogram时,在调用之间分割参数。 - Charles Duffy
@CharlesDuffy:说得好。这可能是一个错误或一个非常需要的功能,具体取决于使用情况。无论如何,我添加了另一种机制。 - Kerrek SB
@CharlesDuffy:是的,确实如此。Bash也有一种让你使用编辑器编辑命令行的方法。而且xargs有许多高级操作方式,可以让你引用参数并逐行读取等等。 - Kerrek SB
@CharlesDuffy:是的,那绝对是我在可重用脚本中要做的。但如果只是一次性使用,你大概知道参数是什么,那么简单的 xargs 通常就足够了。 - Kerrek SB
当然。我试图就正确性提出一个论点,因为StackOverflow的答案本质上是可重用的——理想情况下,它将被很多人阅读和使用(在许多不同的场景中,其中一些用例可能与OP的不同),因此花费时间和精力来确保细节正确是非常值得的。话虽如此,如果我应该添加一个竞争性答案,随时告诉我。 :) - Charles Duffy
@CharlesDuffy:使用ls -1我会用xargs -d \n...但是为什么不发表一个答案呢?那肯定会成为一个合适的答案。 - Kerrek SB

9

在大多数Shell中,您可以使用$(<filename)将文件的内容插入到命令行中:

./myprogram $(<arguments.dat)

如果你的 shell 不支持该功能,那么可以使用旧的方法之一:
./myprogram $(cat arguments.dat)
./myprogram `cat arguments.dat`   # need this one with csh/tcsh

(您知道命令行参数和文件输入之间的区别吗?为什么您希望将命令行参数导入程序中?)
您知道命令行参数和文件输入之间的区别吗?为什么您希望将命令行参数导入程序中?

“older ways” 不会将参数分开。文件中的所有数据将作为单个参数加载。 - osgx
@osgx:那完全不正确。要将其作为单个参数,您需要将其放在双引号内。 - caf
如果您的文件包含"第一个参数" "第二个参数",那么将会把"第一个作为第一个参数,参数"作为第二个参数,"第二个作为第三个参数。如果它包含*.txt,它将被替换为文本文件列表,而不是保持它的字面意义。如果文件包含"*.txt",则引号将作为字符串的文字部分传递(如果用户没有使用nullglobfailglob使前者被视为失败的glob...好吧,如果没有任何以双引号字符开头和结尾的名称的文件,则失败)。 - Charles Duffy

4

如果您不希望参数被默默分割

...也就是说:以下答案适用于不能将./myprogram --first-argument "first value" 悄悄地改变为 ./myprogram --first-argument; ./myprogram "first value"的情况。

如果您的参数是一行文字

也就是说,如果您的输入看起来像:

--first-argument
first value
--second-argument
second value

你的意思是让它运行:

./myprogram --first-argument "first value" --second-argument "second value"

如果你使用的是bash 4.0或更高版本,则应该使用以下命令:

readarray -t args <arguments.dat
./myprogram "${args[@]}"

...或者(对于bash 3.x也是如此):

args=( )
while IFS= read -r arg; do
  args+=( "$arg" )
done <arguments.dat
./myprogram "${args[@]}"

如果您的参数使用引号或转义符进行区分
也就是说,如果您的文件包含类似以下内容(请注意,换行和未加引号的空格在此处的行为相同):
--first-argument "first value"
--second-argument "second value"

如果您希望它运行:

./myprogram --first-argument "first value" --second-argument "second value"

如果您想使用,则应该使用:

args=( )
while IFS= read -r -d '' arg; do
  args+=( "$arg" )
done < <(xargs printf '%s\0' <arguments.dat)

如果你控制你的参数格式
使用NULL分隔的值。也就是说,将文件创建为如下所示:
printf '%s\0' "argument one" "argument two" >arguments.dat

...并按如下方式解析:

args=( )
while IFS= read -r -d '' arg; do
  args+=( "$arg" )
done <arguments.dat
./myprogram "${args[@]}"

即使参数值包含了字面换行、字面引号、字面反斜线或其他不可打印的字符,这个方法也可以处理所有可能的参数值。(在UNIX命令行中不可能使用字面NULs,因为命令行由以NUL结尾的字符串组成;因此,NUL是唯一完全安全的用于在字符串中明确分隔参数的字符)。


如果需要跨调用拆分参数

如果想要的结果是:当文件中有比一个调用程序能够处理的更多的参数时,每个调用程序都接收到一个子集参数,则本小节与您相关。在这种情况下,xargs是正确的工具。

如果在GNU平台上运行,则可以运行xargs -a arguments.dat而不是重定向stdin;然而,在BSD xargs(如MacOS)上不支持此功能,因此此处不进行演示。

如果你的参数是一行一个字面量

对于GNU xargs(大多数Linux平台):

xargs -d $'\n' ./myprogram <arguments.dat

使用BSD xargs(MacOS,FreeBSD/OpenBSD等):

xargs -0 ./myprogram < <(tr '\n' '\0' <arguments.dat)

如果您的参数使用引号或转义符来区分它们
xargs ./myprogram <arguments.dat

如果您已经生成了NUL分隔的输入

xargs -0 ./myprogram <arguments.dat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接