从文件中读取shell参数时保护引号

时间:2015-06-08 22:50:14

标签: arrays bash

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

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

但如果争论来自子壳,我无法正常工作:

$ 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]

我该如何正确地做到这一点?

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

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

所以这个解决方案不起作用:

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

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

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

1 个答案:

答案 0 :(得分:0)

这里没有一个好的解决方案,但你可以选择不好的解决方案。

此答案需要更改文件格式:

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

grunt.config('directory')

如果有效参数不能包含换行符,您可能希望在阅读方面忽略mkdir: { all: { options: { mode: 0777, create: ['<%= directory %>'] } } } 并将写作方# 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 ''以使用换行而不是NUL。请注意,UNIX文件名可以包含换行符,因此如果您的可能参数包含文件名,则此方法是不明智的。

这个答案几乎实现了类似shell的解析语义:

\0

\n有一些围绕多行字符串的极端情况,其解析不像完全与shell的行为完全相同。然而,这是一个99%的解决方案。

这个答案需要一个Python解释器:

Python标准库foo=( ) while IFS= read -r -d '' entry; do foo+=( "$entry" ) done < <(xargs printf '%s\0' <junk) 模块支持符合POSIX标准的字符串标记,这比标准xargs实现的标准更为真实。请注意,shlex等bash / ksh扩展名不受尊重。

xargs

这些答案会带来安全风险:

...具体来说,如果可以编写$'foo'的内容以包含对shell敏感的代码(例如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 /)以这种方式安全阅读,并且您控制写入它的代码,请考虑:

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

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

或者,您可以使用:

foo

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