我有配置文件,其中每行包含由分号分隔的分配。像这样的东西,它模仿正常的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解决方案(没有外部程序调用),但这是一个偏好,而不是一个硬性要求。
如果我解决了这个问题,我知道如何安全地执行间接分配,并且我不需要有关如何读取文件,执行正则表达式匹配等的详细代码。这只是我失踪的关键步骤,而且我希望有一种方法不涉及编写解析器。
答案 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
)。
read
- 没有-r
选项 - 只解释基于\
的序列,除了下面讨论的例外, 在\
序列之前移除 \<char>
;它 不执行控件字符转义序列的扩展,例如\n
。
如果read
之后是实际换行符(LF字符),那么\
执行的排序>>的唯一扩展是 ,其中也删除了换行符,这表示\
的主要目的 - 逃离read
:行继续 。
来自POSIX spec:
默认情况下,除非指定
-r
选项,否则<backslash>
将充当转义字符。未转义的<backslash>
应保留以下字符的字面值,但<newline>
除外。如果<newline>
跟在<backslash>
之后,则读取实用程序应将此解释为行继续。在将输入拆分为字段之前,应删除<backslash>
和<newline>
。将输入拆分为字段后,将删除所有其他未转义的<backslash>
字符。
-r
选项可解释\
序列 off ,这是绝大多数情况下所需的行为。
>
因此,建议定期使用-r
,除非您明确需要处理\
序列。