![如何将多行文件的内容作为参数传递?](https://linux22.com/image/194439/%E5%A6%82%E4%BD%95%E5%B0%86%E5%A4%9A%E8%A1%8C%E6%96%87%E4%BB%B6%E7%9A%84%E5%86%85%E5%AE%B9%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92%EF%BC%9F.png)
我正在尝试运行一个带有-t
参数的脚本。该参数代表文本,并且理论上该值允许是多行的。在命令行上,我假设此处文档可以工作,但我不喜欢在命令行上输入长内容。另外,我希望这个文件能够保留下来,这样我以后可以再次传递它。
我不知道该怎么做;如果我cat foo | xargs echo
,它打印为一行。这解决了这个问题:cat foo | xargs -d='' echo
但它让我觉得有些我不明白的事情会根据文档的内容改变文档的空白或一般结构。
如何将多行文件作为参数传递,而不必担心特殊字符或更改其格式?
答案1
为什么不直接传递一个文件名呢?如果脚本尚不知道如何处理文件名,请对其进行修改(例如,将所有未知参数视为文件名,和/或添加
-f
输入文件名选项)。请记住添加错误检查代码并在文件名不存在或由于权限而无法读取等情况下做出适当的响应。引用你的论点。例如
-t "$(cat foo)"
答案2
xargs -d=''
(其中-d
是 的 GNU 实现的扩展)与shell 语言中的引用语法xargs
相同。这告诉我们用作分隔符。xargs -d=
''
xargs
=
例如,echo foo=bar | xargs -d= cmd
将cmd
使用foo
和bar<newline>
作为参数进行调用。
使用xargs -d ''
or xargs --delimiter=
(可以缩写为xargs --del=
,甚至xargs --d=
在当前版本中,xargs
因为目前没有其他以 开头的长选项d
),您会收到语法错误。
您可以使用xargs -d '\0'
,这与更可移植的(尽管仍然不是标准的)相同xargs -0
,它使用 NUL 字符作为分隔符。 NUL 字符不应该出现在文本文件中,并且无论如何都不能在参数中传递给非内置命令,因为参数作为 NUL 分隔字符串传递给系统execve()
调用。
所以:
xargs -I'<TEXT>' -0a file cmd -t '<TEXT>' other args
(-a
作为另一个 GNU扩展),会将as 参数xargs
的确切内容传递给²。file
cmd -t
但如果file
包含一个foobar
线例如,这将传递整行,包括行分隔符 ( "foobar\n"
)。
或者,你可以这样做(在类似 POSIX 的 shell 中):
cmd -t "$(cat file)" other args
命令替换确实会从内部命令的输出中删除所有尾随换行符,因此可能更可取。如果输出包含 NUL 字符,某些 shell,例如bash
删除它们(使用"$(tr -d '\0' < file)"
而不是在任何 shell 中获取该行为)。
请注意,它周围的双引号很重要。如果没有他们,扩张将受到影响分裂+全局(分裂仅在 zsh 中)如果文件包含$IFS
( 和新队是$IFS
) 或通配符的默认值。
在 ksh、zsh 或 bash 中,您还可以使用 来"$(<file)"
代替通过自身读取文件来"$(cat file)"
优化执行cat
(在 bash 中,这仍然在子进程中完成)。
在 中zsh
,您还可以在模块$mapfile
中使用特殊的关联数组zsh/mapfile
:
zmodload zsh/mapfile
cmd -t "$mapfile[path/to/file]" other args
即按原样传递内容,包括 NUL(这将导致execve()
在第一个 NUL 之后截断 arg)和尾随换行符。
在rc
shell 或衍生工具中,您可以执行以下操作:
cmd -t ``(){cat file} other args
其中``(sep)cmd
是命令替换的变体(在`cmd
那里),您可以在其中指定分隔符,此处没有。该 shell 中不会删除尾随换行符,因此整个文件内容将按原样传递。
无论如何,请注意,在大多数系统上,命令的参数 3 的总大小都有限制(尽管在最新版本的 Linux 上,可以通过更改堆栈大小限制),以及在 Linux 上单个参数的大小(最大 128KiB)。
现在,要按字面意思传递多行字符串而不必担心特殊字符,您可以执行以下操作:
cmd -t 'the multiline here
where the only character you have to
worry about in Bourne-like shells is
single quote which you have to enter
as '\'', that is leave the quotes, enter
it with other quoting operators (here \)
and resume the quotes'
在rc
shell 中(其中'...'
是唯一的引号形式)或启用zsh
该选项时,可以在单引号内输入单引号,例如:。看rcquotes
''
cmd -t 'It''s simpler like that'
如何在 Unix shell 中像普通字符一样使用特殊字符?有关在各种 shell 中引用的详细信息。
或者您可以使用此处文档,将其存储在变量中:
multi=$(cat << 'EOF')
multiline string here, only worry would be about
an EOF line by itself though also note that
all trailing newlines, so that includes all
trailing empty lines are removed, including these:
EOF
)
cmd -t "$multi" # note the quotes again
或者直接作为:
cmd -t "$(cat << 'EOF'
multi
line
here
EOF
)"
请注意其中的引号EOF
。如果没有它们,参数扩展(如$var
)、命令替换(如$(cmd)
或`cmd`
)和算术扩展($((...))
)仍然会执行。
在mksh
shell 中,您可以使用:
cmd -t "$(<< 'EOF'
multi
line
EOF
)"
其中cat
和 fork 被优化,使其本质上成为多行报价形式。
¹ 它不能在内置命令或函数的参数中传递,甚至不能存储在大多数 shell 的变量中,zsh
这是我所知道的唯一例外。
² 只要文件不为空。如果文件为空,则-I
根本不运行该命令,您需要该文件包含一个 NUL 字符,以便使用一个空参数调用该命令。
³ 从技术上讲,限制(execve()
再次在系统调用中,因此不适用于 shell 内置函数/函数)是参数的累积大小和环境并且通常还会考虑到每个参数和 envvar 字符串的指针的大小,因此通常很难提前预测一组特定的参数是否会突破限制。