从文件中读取Shell参数时要遵守引号 [英] Honoring quotes while reading shell arguments from a file

查看:233
本文介绍了从文件中读取Shell参数时要遵守引号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在bash中,我可以将带引号的参数传递给这样的命令:

In bash, I can pass quoted arguments to a command like this:

$ printf '[%s]\n' 'hello world'
[hello world]

但是,如果参数来自子shell,我将无法正常工作

But I can't get it to work right if the argument is coming from a subshell:

$ cat junk
'hello world'
$ printf '[%s]\n' $(cat junk)
['hello]
[world']

或者:

$ cat junk
hello world
$ printf '[%s]\n' $(cat junk)
[hello]
[world]

或者:

$ cat junk
hello\ world
$ printf '[%s]\n' $(cat junk)
[hello\]
[world]

如何正确执行此操作?

解决方案还需要处理这种情况:

The solution also needs to handle this case:

$ printf '[%s]\n' abc 'hello world'
[abc]
[hello world]

因此该解决方案不起作用:

So this solution doesn't work:

$ cat junk
abc 'hello world'
$ printf '[%s]\n' "$(cat junk)"
[abc 'hello world']


Bash引用问题中的问题已被建议重复.但是,目前尚不清楚如何应用其已接受的答案.以下失败:


The question at Bash quoting issue has been suggested as a duplicate. However, it isn't clear how to apply its accepted answer; the following fails:

$ cat junk
abc 'hello world'
$ FOO=($(cat junk))
$ printf '[%s]\n' "${FOO[@]}"
[abc]
['hello]
[world']

推荐答案

这里没有一个好的解决方案,但是您可以在不好的解决方案之间进行选择.

There's no one good solution here, but you can choose between bad ones.

为文件使用NUL分隔的流是最安全的方法.从字面上看,任何C字符串(因此,任何bash字符串都可以存储为数组元素)都可以用这种方式进行读写.

Using a NUL-delimited stream for the file is the safest approach; literally any C string (thus, any string bash can store as an array element) can be written and read in this manner.

# write file as a NUL-delimited stream
printf '%s\0' abc 'hello world' >junk

# read file as an array
foo=( )
while IFS= read -r -d '' entry; do
  foo+=( "$entry" )
done <junk

如果有效参数不能包含换行符,则您可能希望忽略读取端的-d ''并将书写端的\0更改为\n以使用换行符代替NUL.请注意,UNIX文件名可以包含换行符,因此,如果可能的参数包括文件名,则这种方法是不明智的.

If valid arguments can't contain newlines, you may wish to leave out the -d '' on the reading side and change the \0 on the writing side to \n to use newlines instead of NULs. Note that UNIX filenames can contain newlines, so if your possible arguments include filenames, this approach would be unwise.

foo=( )
while IFS= read -r -d '' entry; do
  foo+=( "$entry" )
done < <(xargs printf '%s\0' <junk)

xargs在围绕多行字符串的一些极端情况下,其解析与外壳的解析并不完全相同.但是,这是99%的解决方案.

xargs has some corner cases surrounding multi-line strings where its parsing isn't quite identical to how a shell does. It's a 99% solution, however.

Python标准库shlex模块支持POSIX兼容的字符串标记化,与xargs实现的标准相比,它更符合标准.请注意,不支持bash/ksh扩展名,例如$'foo'.

The Python standard library shlex module supports POSIX-compliant string tokenization which is more true to the standard than that implemented by xargs. Note that bash/ksh extensions such as $'foo' are not honored.

shlex_split() {
  python -c '
import shlex, sys
for item in shlex.split(sys.stdin.read()):
    sys.stdout.write(item + "\0")
'
}
while IFS= read -r -d '' entry; do
  foo+=( "$entry" )
done < <(shlex_split <junk)


这些答案构成安全风险:

...特别是,如果junk的内容可以编写为包含对外壳敏感的代码(例如$(rm -rf /)),则您不想使用它们中的任何一个:


These answers pose a security risk:

...specifically, if the contents of junk can be written to contain shell-sensitive code (like $(rm -rf /)), you don't want to use either of them:

# use declare
declare "foo=($(cat junk))"

# ...or use eval directly
eval "foo=( $(cat junk) )"


如果要确保以安全的方式读取foo,并且可以控制写入其中的代码,请考虑:


If you want to be sure that foo is written in a way that's safe to read in this way, and you control the code that writes to it, consider:

# write foo array to junk in an eval-safe way, if it contains at least one element
{ printf '%q ' "${foo[@]}" && printf '\n'; } >junk;


或者,您可以使用:


Alternately, you could use:

# write a command which, when evaluated, will recreate the variable foo
declare -p foo >junk

和:

# run all commands in the file junk
source junk

这篇关于从文件中读取Shell参数时要遵守引号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆