从配置文件中读取分配

时间:2017-02-19 13:42:15

标签: bash shell

我有配置文件,其中每行包含由分号分隔的分配。像这样的东西,它模仿正常的shell赋值:

VAR1="1"  ;  VAR2="2"
VAR1="3"  ;  VAR2="4"

每一行包含相同的变量,并且要单独处理。这些配置文件都在系统管理员控制之下,因此使用eval执行分配对于现在来说并不算太糟糕。但我想将此扩展到每用户配置文件,我正在寻找更好的想法。

我能够解析一行,使用;作为分隔符将其拆分(不幸的是,这种方式不允许在值中找到转义的;,但我可以活使用那个),识别赋值(有效变量名后跟=符号),并提取赋值的右侧部分(原始形式,引号和间距作为值的一部分)。但后来我遇到了问题。

假设我有变量value,在解析之后,它包含这样的“手动”赋值所产生的内容:

value="\"Arbitrary value \\\" containing escaped quote inside quotes\""

换句话说,值是这个(如果我echo "$value"):

"Arbitrary value \" containing escaped quote inside quotes"

我希望在不使用eval或其他可能导致任意代码执行的方法(以及代码注入风险)的情况下转换该值,以便它成为:

Arbitrary value " containing escaped quote inside quotes

我想,我可以只查找并删除引号和尾随引号,但这并不能处理所有有效shell引用的情况。如果有一种方法可以在防止代码执行的同时保留安全扩展,那么这是一个优点,但我对此并不抱有希望。我也更喜欢Bash-only解决方案(没有外部程序调用),但这是一个偏好,而不是一个硬性要求。

如果我解决了这个问题,我知道如何安全地执行间接分配,并且我不需要有关如何读取文件,执行正则表达式匹配等的详细代码。这只是我失踪的关键步骤,而且我希望有一种方法不涉及编写解析器。

2 个答案:

答案 0 :(得分:2)

一个非常简单的解决方案是使用jq。因为" foo是一个字符串\"包含引用"是有效的json,它本地处理它:

$ value="\"Arbitrary value \\\" containing escaped quote inside quotes\""
$ jq -r . <<< "$value"
Arbitrary value " containing escaped quote inside quotes

是的,它不是原生的sh或bash,但它是一种快速简便的解决方案。此外,jq具有将结果输出回可由另一个shell读取的格式的方法:

$ jq -r '.|@sh' <<< "$value"
'Arbitrary value " containing escaped quote inside quotes'

答案 1 :(得分:1)

使用bash解决方案来补充kojiro's helpful jq solution(也可以使用符合POSIX标准的实施方式):

# Sample value, resulting in the following value, *including* the double quotes:
#     "Arbitrary value \" containing escaped quote inside quotes"
# Note: This is effectively the same assignment as in the question, except
#       with single quotes, which makes it easier to parse visually.
value='"Arbitrary value \" containing escaped quote inside quotes"'    

# Strip enclosing " instances, if present.
[[ $value =~ ^\"(.*)\"$ ]] && value=${BASH_REMATCH[1]}

# Use `read` - without -r - to perform interpretation of \-prefixed
# escape sequences, and save the result back to $value.
IFS= read value <<<"$value"

之后运行printf '%s\n' "$value"会产生:

Arbitrary value " containing escaped quote inside quotes

注意:

  • 如果$value包含\后跟实际换行符(可能不关心配置文件条目),则该换行符将为除去

  • 对于任何其他\ - 前缀字符 - 不仅仅是\" - (仅限)\已删除。

  • 不执行任何类型的扩展,并且不支持 shell 支持的其他字符串格式(例如自动连接相邻字符串"ab""cd"以产生abcd)。

    • 请参阅我的this answer以获取安全模板解决方案,该解决方案限制对嵌入式变量引用的扩展(防止命令替换)。

可选背景信息

read - 没有-r选项 - 只解释基于\的序列,除了下面讨论的例外, \序列之前移除 \<char>;它 执行控件字符转义序列的扩展,例如\n

如果read之后是实际换行符(LF字符),那么\ 执行的排序>的唯一扩展是 ,其中也删除了换行符,这表示\主要目的 - 逃离read:行继续
来自POSIX spec

  

默认情况下,除非指定-r选项,否则<backslash>将充当转义字符。未转义的<backslash>应保留以下字符的字面值,但<newline>除外。如果<newline>跟在<backslash>之后,则读取实用程序应将此解释为行继续。在将输入拆分为字段之前,应删除<backslash><newline>。将输入拆分为字段后,将删除所有其他未转义的<backslash>字符。

-r选项可解释\序列 off ,这是绝大多数情况下所需的行为。
> 因此,建议定期使用-r ,除非您明确需要处理\序列。