如何使用 sed 用方括号替换值?

如何使用 sed 用方括号替换值?

我需要在文件中用方括号替换变量:

a=0
foo[1]=0
foo[2]=0

我想替换foo[1]=0foo[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如果你设置了并且 目录中有foo1foo5(和/或),情况会更糟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]将匹配foo1foo2foo5。仅foo[1]匹配。foo1

为了使sed匹配[]字面意思一致,你需要在模式中对它们进行转义。而不是field="foo[$n]"你需要

field="foo\[$n\]"

sed两种情况都可以使用单个命令吗?

是的。您当前的命令就像这样,sed "s/^\($field=\).*/\1$newvalue/"只要您记住 的值$field将被解释为模式的一部分(因此[.\($是特殊的),并且 的值$newvalue将被解释为替换的一部分(因此 eg\1是特殊的),它就会起作用。并且您选择的分隔符(/)不能出现在两个变量中的任何一个中,否则它会破坏或更改语法。


最后说明

单独的第二个修复(在[和之前添加反斜杠])甚至会阻止shell扩展[…],因此它会意外地“修复”在这个特定情况下,对于这个变量的特定值的引用问题。

但一般来说,你应该同时应用这两种修复方法。你当然应该习惯默认引用变量,这将节省你调试未来 shell 脚本的时间和挫败感。这就是为什么我的答案不局限于第二种修复方法。

相关内容