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

Posted

技术标签:

【中文标题】如何使用文件的行作为命令的参数?【英文标题】:How do I use the lines of a file as arguments of a command? 【发布时间】:2011-05-12 19:06:17 【问题描述】:

说,我有一个文件 foo.txt 指定 N 参数

arg1
arg2
...
argN

我需要将其传递给命令my_command

【问题讨论】:

“arguments”,复数,或“argument”,单?接受的答案仅在单参数情况下是正确的 - 这是正文要查询的内容 - 但在主题似乎要询问的多参数情况下不是正确的。 下面的 4 个答案通常具有相同的结果,但它们的语义略有不同。现在我记得我为什么停止编写 bash 脚本了:P 【参考方案1】:

如果你的 shell 是 bash(以及其他),$(cat afile) 的快捷方式是 $(< afile),所以你会写:

mycommand "$(< file.txt)"

记录在 bash 手册页的“命令替换”部分。

或者,从标准输入读取你的命令,所以:mycommand &lt; file.txt

【讨论】:

为了迂腐,使用&lt; 运算符不是捷径。这意味着 shell 本身将执行重定向,而不是针对其重定向属性执行 cat 二进制文件。 这是一个内核设置,因此您需要重新编译内核。或者调查 xargs 命令 @ykaner 因为没有"$(…)"file.txt 的内容将被传递给mycommand 的标准输入,而不是作为参数。 "$(…)" 表示运行命令并将输出作为字符串返回;这里的“命令”只读取文件,但它可能更复杂。 除非我丢失引号,否则它对我不起作用。使用引号,它将整个文件作为 1 个参数。没有引号,它将每一行解释为一个单独的参数。 @lstyls:我不确定您所说的“这不是捷径”是什么意思。让 shell 执行重定向而不是生成子 cat 进程听起来会节省资源。裁判员将这种替换描述为“等效但更快”。也许您的意思是“这不仅仅是键入相同内容的更短方式”?【参考方案2】:

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

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

a "b c" d

你会得到的参数是:

a
"b
c"
d

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

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

【讨论】:

我应该提到,我假设您使用的是 bash。我意识到那里还有其他的 shell,但几乎所有我使用过的 *nix 机器都运行 bash 或类似的。 IIRC,这个语法在 ksh 和 zsh 上应该是一样的。 Read 应该是 read -r 除非你想扩展反斜杠转义序列——并且 NUL 是比换行符更安全的分隔符,特别是如果你传递的参数是文件名之类的东西,可以包含文字换行符。此外,如果不清除 IFS,您会从 i 中隐式清除前导和尾随空格。【参考方案3】:
command `< file`

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

for line in `cat input_file`; do some_command "$line"; done

或者(多行变体):

for line in `cat input_file`
do
    some_command "$line"
done

或者(使用$() 而不是`` 的多行变体):

for line in $(cat input_file)
do
    some_command "$line"
done

参考资料:

    for 循环语法:https://www.cyberciti.biz/faq/bash-for-loop/

【讨论】:

另外,将&lt; file 放在反引号中意味着它实际上根本不会对command 执行重定向。 (赞成这个的人真的测试过吗?command `&lt; file` 在 bash 或 POSIX sh 中都不起作用;zsh 是另一回事,但这不是壳这个问题是关于)。 @CharlesDuffy 你能更具体地说明它是如何不起作用的吗? @CharlesDuffy 这在 bash 3.2 和 zsh 5.3 中有效。这在 sh、ash 和 dash 中不起作用,但在 $( 中也不起作用 @mwfearnley,很高兴提供这种特殊性。目标是遍历行,对吗?将Hello World 放在一行中。你会看到它首先运行 some_command Hello,然后是 some_command Worldnot some_command "Hello World"some_command Hello World【参考方案4】:

你使用反引号来做到这一点:

echo World > file.txt
echo Hello `cat file.txt`

【讨论】:

这不会创建 an 参数——因为它没有被引用,它会被字符串分割,所以如果你在第一步发出 echo "Hello * Starry * World" &gt; file.txt,你会得到至少四个单独的参数传递给第二个命令——而且可能更多,因为*s 将扩展为当前目录中存在的文件的名称。 ...因为它正在运行 glob 扩展,所以它不会发出确切文件中的内容。 而且 因为它正在执行命令替换操作,所以效率极低——它实际上是 fork() 关闭一个附加到其标准输出的 FIFO 的子外壳,然后调用 /bin/cat 作为该子外壳的子外壳,然后通过 FIFO 读取输出;与 $(&lt;file.txt) 相比,后者直接将文件内容读入 bash,不涉及子 shell 或 FIFO。【参考方案5】:

如果您想以一种适用于每个可能的命令行参数(带有空格的值、带有换行符的值、带有文字引号字符的值、不可打印的值、带有全局字符的值等)的稳健方式执行此操作,它变得更有趣了。


要写入文件,给定参数数组:

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 <file
run_your_command "$args[@]"

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

set --
while IFS='' read -r -d '' item; do
  set -- "$@" "$item"
done <file
run_your_command "$@"

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

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 <file)"
run_your_command "$@"

【讨论】:

【参考方案6】:

如果您需要做的只是将文件arguments.txt 转成内容

arg1
arg2
argN

进入my_command arg1 arg2 argN,然后你可以简单地使用xargs

xargs -a arguments.txt my_command

您可以在xargs 调用中添加额外的静态参数,例如xargs -a arguments.txt my_command staticArg,它将调用my_command staticArg arg1 arg2 argN

【讨论】:

【参考方案7】:

以下是我将文件内容作为参数传递给命令的方法:

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

【讨论】:

这 - 但效率大大降低 - 等效于 $(&lt;bar.txt)(当使用带有适当扩展名的 shell,例如 bash)。 $(&lt;foo) 是一种特殊情况:与$(cat ...) 中使用的常规命令替换不同,它不会派生子shell 进行操作,因此避免了大量开销。【参考方案8】:

似乎没有一个答案对我有用,或者太复杂了。幸运的是,xargs 并不复杂(在 Ubuntu 20.04 上测试)。

正如 OP 所提到的,这适用于文件中单独一行上的每个 arg,这也是我所需要的。

cat foo.txt | xargs my_command

需要注意的一点是,它似乎不适用于别名命令。

如果命令接受包含在字符串中的多个参数,则接受的答案有效。在我使用 (Neo)Vim 的情况下,它没有,并且所有参数都粘在一起。

xargs 正确执行,实际上为您提供了提供给命令的单独参数。

【讨论】:

【参考方案9】:

在我的 bash shell 中,下面的工作就像一个魅力:

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

input_file 在哪里

arg1
arg2
arg3

很明显,这允许您使用 input_file 中的每一行执行多个命令,这是我学到的一个不错的小技巧 here。

【讨论】:

从安全角度来看,您的“小技巧”很危险——如果您有一个包含$(somecommand) 的参数,您将执行该命令而不是作为文本传递。同样,&gt;/etc/passwd 将作为重定向处理并覆盖/etc/passwd(如果以适当的权限运行)等。 执行以下操作会更安全(在具有 GNU 扩展的系统上):xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _ -- 并且效率更高,因为它向每个 shell 传递尽可能多的参数,而不是启动一个输入文件中每行的 shell。 当然,丢掉useless use of cat【参考方案10】:

我建议使用:

command $(echo $(tr '\n' ' ' < parameters.cfg))

只需修剪结束行字符并将它们替换为空格,然后将生成的字符串作为可能的单独参数与 echo 推送。

【讨论】:

【参考方案11】:

在编辑@Wesley Rice's answer 几次之后,我决定我的更改太大了,无法继续更改他的答案,而不是自己写。所以,我决定我需要自己写!

读取文件的每一行并像这样逐行操作:

#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
  echo "$line"
done < "$input"

这直接来自作者 Vivek Gite:https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/。他得到了荣誉!

语法:在 Bash Unix 和 Linux shell 上逐行读取文件: 1. bash、ksh、zsh等shell逐行读取文件的语法如下 2.while read -r line; do COMMAND; done &lt; input.file 3. 传递给读取命令的-r 选项可防止反斜杠转义被解释。 4.在读取命令前添加IFS=选项,防止前导/尾随空格被修剪- 5.while IFS= read -r line; do COMMAND_on $line; done &lt; input.file


现在来回答这个我也有的现已结束的问题:Is it possible to `git add` a list of files from a file? - 这是我的答案:

请注意,FILES_STAGED 是一个变量,其中包含一个文件的绝对路径,该文件包含一堆行,其中每一行都是我想要在 git add 上执行的文件的相对路径。这段代码 sn-p 即将成为该项目中 "eRCaGuy_dotfiles/useful_scripts/sync_git_repo_to_build_machine.sh" 文件的一部分,以便从一台 PC 轻松同步开发中的文件(例如:我的计算机代码)到另一个(例如:我构建的更强大的计算机):https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles。

while IFS= read -r line
do
    echo "  git add \"$line\""
    git add "$line" 
done < "$FILES_STAGED"

参考资料:

    我从哪里复制了我的答案:https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ For 循环语法:https://www.cyberciti.biz/faq/bash-for-loop/

相关:

    如何逐行读取文件内容并对其执行git add:Is it possible to `git add` a list of files from a file?

【讨论】:

以上是关于如何使用文件的行作为命令的参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C++ 中的流从文件末尾读取给定数量的行?

如何仅使用命令行参数读取唯一行?

如何使用 CMake 获取命令行输出作为参数?

Python - 如何获取文本文件中的行数[重复]

如何在Unix下使用sed命令

如何使用不同的参数在命令中执行 python 文件