我需要在文件中用方括号替换变量:
a=0
foo[1]=0
foo[2]=0
我想替换foo[1]=0
为foo[1]=2
。
要更改我使用的值a
:
func_update_value () {
field=$1
newvalue=$2
sed -i "s/^\($field=\).*/\1$newvalue/" file
}
field="a"
value="2"
func_update_value $field $value
foo[x]
但同样的命令对...不起作用。
func_update_value () {
field=$1
newvalue=$2
sed -i "s/^\($field=\).*/\1$newvalue/" file
}
n=1
field="foo[$n]"
value="2"
func_update_value $field $value
正确的语法是什么sed
?
更改是由函数进行的,是否可以sed
对两种情况使用单个命令?
答案1
[1]
在 shell 中不是(即不一定)文字。那么它在 中作为正则表达式模式的一部分就不是(即肯定不是)文字sed
。
我检查了你之前的问题,我猜你使用的是 Bash。我的回答的第一部分适用于许多常见的 shell,包括 Bash(zsh 是明显的例外)。
shell 中可能存在问题
首先给变量赋值:
n=1
field="foo[$n]"
value="2"
所有这些都按预期工作。不带引号的部分1
并不特殊,不需要加引号。带引号的部分2
可能会或可能不会加引号,这没什么区别。带双引号的部分foo[$n]
会有所不同;有双引号很好,这样[
也]
没什么特别的,变量的内容field
肯定是字面上foo[1]
的。
但你有
func_update_value $field $value
其中$field
没有引用。参数扩展之后的行是
func_update_value foo[1] 2
进而模式匹配针对当前目录中的对象(通配符)开始起作用,尤其是:
[…]
匹配任意一个括起来的字符。
这意味着如果你有一个名为的文件或目录foo1
,该行将被评估为
func_update_value foo1 2
n=125
如果你设置了并且 目录中有foo1
和foo5
(和/或),情况会更糟foo2
。多个文件将与该foo[125]
模式匹配,并且该行的最终形式将类似于
func_update_value foo1 foo5 2
这绝对不是你想要的。你大概当前目录中没有任何名称foo1
,因此模式此时保持文字状态,并且行
func_update_value foo[1] 2
func_update_value
使用文字参数foo[1]
和运行该函数2
。
如果您的代码此时没有失败,那只是偶然没有出现foo1
在目录中,而不是因为编码不正确。正确的编码包括双引号所有变量。在极少数情况下,你不想引用,如果你知道自己在做什么; 这不是其中的一个。
还要注意函数本身中没有引用的field=$1
地方$1
。未引用的问题$1
与之前未引用的问题相同$field
。
问题sed
假设您的函数正确地获得了文字foo[1]
参数,并且其局部变量现在包含应有的field
文字字符串。foo[1]
问题是,[1]
作为正则表达式模式的一部分,sed
不是文字。就像在 shell 中一样,它匹配任何一个括起来的字符(除了匹配的是文本,而不是文件名)。foo[125]
将匹配foo1
或foo2
或foo5
。仅foo[1]
匹配。foo1
为了使sed
匹配[
和]
字面意思一致,你需要在模式中对它们进行转义。而不是field="foo[$n]"
你需要
field="foo\[$n\]"
sed
两种情况都可以使用单个命令吗?
是的。您当前的命令就像这样,sed "s/^\($field=\).*/\1$newvalue/"
只要您记住 的值$field
将被解释为模式的一部分(因此[
、.
、\(
等$
是特殊的),并且 的值$newvalue
将被解释为替换的一部分(因此 eg\1
是特殊的),它就会起作用。并且您选择的分隔符(/
)不能出现在两个变量中的任何一个中,否则它会破坏或更改语法。
最后说明
单独的第二个修复(在[
和之前添加反斜杠]
)甚至会阻止shell扩展[…]
,因此它会意外地“修复”在这个特定情况下,对于这个变量的特定值的引用问题。
但一般来说,你应该同时应用这两种修复方法。你当然应该习惯默认引用变量,这将节省你调试未来 shell 脚本的时间和挫败感。这就是为什么我的答案不局限于第二种修复方法。