【问题标题】:replace substring in lines using sed or grep使用 sed 或 grep 替换行中的子字符串
【发布时间】:2012-11-08 12:16:01
【问题描述】:

我有一个包含很多行的文件,其中两个是:

videoId: 'S2Rgr6yuuXQ'

var vid_seq=1;

在一个 shell 脚本中,我有两个变量, 对于 id,值始终为 11 个字符/数字

id='fsafsferii2'

id_seq=80

我想用id和id_seq修改这两行

videoId: 'fsafsferii2'

var vid_seq=80;

我用过

sed -i 's/\(videoId: \).*\\1'${id}'/\2' file

但是有错误,我的脚本有什么问题?

谢谢

【问题讨论】:

  • sed:-e 表达式 #1,字符 33:未终止的 `s' 命令
  • 对。您正在使用 / 作为替换的分隔符。该命令是 s/pattern/replacement/ 并且您缺少最终分隔符。这就是“未终止”的意思。
  • 我加了/还是错了

标签: shell sed grep


【解决方案1】:

grep 命令不会“替换”文本,它用于“全局正则表达式打印”。但 sed 会。

sed -i'' '/^videoId: /s/: .*/: '"$id"'/;/^var vid_seq=/s/=.*/='"$id_seq"';/'

我不太喜欢以这种方式将变量插入到 sed 脚本中,但 sed 很简单,并且没有提供实际使用实际变量的机制。如果要执行此操作,请在运行此 sed 脚本之前对这两个变量进行一些格式检查,以确保它们包含您希望它们包含的数据。变量中的意外/ 会导致 sed 脚本失败。

更新每个 cmets:

这是一个成功的测试:

$ id=fsafsferii2
$ id_seq=80
$ cat inp686

videoId: 'S2Rgr6yuuXQ'

var vid_seq=1;

$ sed '/^videoId: /s/: .*/: '"$id"'/;/^var vid_seq=/s/=.*/='"$id_seq"';/' < inp686

videoId: fsafsferii2

var vid_seq=80;

$ 

当然,您需要做一些引用魔术才能将单引号添加到您的 videoId 中,但我相信您可以自己解决。

更新 2

根据 sed 的手册页,替换命令的格式为:

     [2addr]s/regular expression/replacement/flags

[2addr] 表示您最多可以指定两个“地址”,可以是要匹配的行号或正则表达式。因此s(替代)命令可以采用一行、一个范围、一个匹配或匹配之间的跨度。在我们的例子中,我们只是使用一个匹配来确定我们想要在哪些行上执行替换。

上面的脚本由两个sed命令组成,用分号隔开。

  • /^videoId: / -- 匹配以单词videoId:开头的行...
    • s/: .*/: '"$id"'/; -- 用$id 环境变量中的任何内容替换从冒号到行尾的所有文本。
  • /^var vid_seq=/ -- 匹配符合...的行,如上。
    • s/=.*/='"$id_seq"';/ -- 将等号中的所有文本替换为 $id_seq

注意'"$id"' 构造意味着我们退出单引号,然后立即进入双引号以扩展变量...然后退出双引号并返回到一组新的单引号。 sed 脚本在单引号内是最安全的,因为它经常使用可能被 shell 解释的字符。

还要注意,由于 sed 的替换命令使用正斜杠作为分隔符,$id$id_seq 变量可能不包含斜杠。如果可能,您可以切换到不同的分隔符。

【讨论】:

  • 在我根据您的问题创建的测试环境中为我工作。如果您尝试过并得到不同的错误,那么update your question 并显示结果。不要忘记显示您的实际变量。如果我无法重现您的错误,我将无法修复它。
  • 啊,我在videoId之前修改了^ -> .* coz,有很多空字符,现在可以了。顺便说一句,你能解释一下脚本吗
  • @user1769686 - 添加了解释。
【解决方案2】:

有什么问题:

sed -i 's/\(videoId: \).*\\1'${id}'/\2' file
  1. 缺少第三个分隔符 (/)。有效语法为s/regex/replace/
  2. 不正确的正则表达式模式(假设${id} 已被替换)

    \(videoId: \).*\\1fsafsferii2
    

    告诉它匹配一个看起来像这样的字符串:

    videoId: anything\1fsafsferii2
    

    (正则表达式中的\\ 匹配文字反斜杠,因此\\1 将匹配文字反斜杠,后跟1 而不是第一个子表达式)

  3. 将匹配的字符串替换为\2

    但是由于只有一组括号,\2实际上是空的。

    此外,由于 2. 中的正则表达式模式不匹配任何内容,因此没有任何内容被替换。


这应该可以工作(GNU sed)

sed -i 's/\(videoId: \).*/\1 \x27'${id}'\x27/
        s/\(var vid_seq=\).*/\1'${id_seq}'\;/' file

注意:

  • \x27 是单引号的十六进制表示(防止与其他单引号冲突)
  • \; 用于文字分号。如果; 未转义,则解释为终止sed 中的s 命令。

【讨论】:

  • \x27 对我不起作用。当我echo hello | sed 's/e/\x26/'时,结果是hx26llo
  • 我猜一些旧版本的 sed 不支持这种十六进制表示法。
  • 这是 FreeBSD 9.1-RC3 中的 sed。我在 OSX Mountain Lion 中得到了同样的行为。也许你正在运行一个非标准的 sed。
猜你喜欢
  • 1970-01-01
  • 2014-01-13
  • 2018-02-24
  • 1970-01-01
  • 2020-03-04
  • 2011-09-04
  • 2012-12-21
  • 1970-01-01
  • 2020-02-18
相关资源
最近更新 更多