【问题标题】:Error while executing sed command执行 sed 命令时出错
【发布时间】:2015-11-24 15:09:23
【问题描述】:

我正在尝试使用命令执行脚本:

sed -i "USER/c\export USER=${signumid}" .bashrc

sed -i "DEVENVHOME=$/c\export DEVENVHOME=${DEVENVHOME:-/home/${signumid}/CPM_WORKAREA/devenv.x}" .bashrc
 

我想将.bashrc 中的字符串“USER”替换为export USER=${signumid},其中$signumid 变量是通过Cygwin 提示符提供的。同样,我想在.bashrc 文件中用export DEVENVHOME=${DEVENVHOME:-/home/${signumid}/CPM_WORKAREA/devenv.x} 替换字符串DEVENVHOME=$ 的行,其中$signumid 变量是通过Cygwin 提示符提供的。

但我在 Cygwin 终端上遇到以下错误。:

 sed: -e expression #1, char 1: unknown command: `U'
 sed: -e expression #1, char 3: extra characters after command

【问题讨论】:

  • 是的,这些是语法错误。 根本不清楚您希望这个无效代码应该完成什么,因此我们只能重述错误消息已经说明的内容,无法为您提供更多帮助。您可以通过解释您希望程序做什么来改进您的问题。
  • 替代命令默认为s/replace-this/with-that/sed 程序告诉你,它的曲目中没有命令 U;您应该改用sed -i 's/USER/export USER='"${signumid}"/ .bashrc 之类的东西。目前尚不清楚c\ 的用途。同样在第二个命令中,除了你可能不应该使用 / 作为字符,因为替换字符串包含它;请改用%@ 或其他东西(control-A?)。
  • 我尝试执行 sed -i 's/USER/export USER='"${signumid}"/ .bashrc ,但它给出错误 sed: -e expression #1, char 35: unknown option到“s”,现在。
  • 听起来像signumid 包含一个斜线。我的回答最后已经涵盖了这一点。

标签: shell sed cygwin


【解决方案1】:

sed 脚本的一般语法是一系列 address command arguments 语句(由换行符或分号分隔)。最常见的命令s 替换命令,带有一个空的地址, 所以我们也许可以假设这就是你想在这里使用的。您似乎正在尝试插入一个 shell 变量 $signumid,这给这个说明增加了一点复杂性。

如果您的字符串只是静态文本,那么使用单引号是有意义的;然后,shell 根本不会更改引号内的文本。 s 命令的一般语法是 s/regex/replacement/,其中作为参数分隔符的斜杠只是一个占位符,我们很快就会看到。

sed -i 's/.*USER.*/export USER=you/
    s% DEVENVHOME=\$%export DEVENVHOME=${DEVENVHOME:-/home/you/CPM_WORKAREA/devenv.x}%' .bashrc

这将找到任何带有USER 的行并将整行替换为export USER=you;然后找到包含DEVENVHOME=$ 的任何行(前面有一个空格,和一个文字美元字符)并将 匹配的表达式 替换为长字符串。因为替换字符串在内部使用斜杠,所以我们使用不同的正则表达式分隔符%——或者,我们可以反斜杠转义不是分隔符的斜杠,但正如我们将看到的,当我们添加以下扭曲时,这很快就会变得站不住脚。因为美元符号在正则表达式中作为“行尾”元字符具有重要意义,所以我们将其反斜杠转义。

我在您的尝试中忽略了c\,假设这只是对sed 语法的误解。如果它很重要,你希望用它完成什么? c\export 不是一个有效的 Bash 命令,所以你可能有别的意思,但我猜不出是什么。

现在,要将 shell 变量 signumid 的值内插到替换中,我们不能使用单引号,因为它们会抑制内插。您已正确尝试使用双引号(在您编辑的问题中),但这意味着我们必须进行一些额外的更改。在双引号内,反斜杠由 shell 处理,因此我们需要将所有反斜杠加倍,或者寻找替代结构。对我们来说幸运的是,唯一的反斜杠在\$ 中,它可以等效地表示为[$],所以让我们改用那个符号。此外,如果替换字符串中需要文字美元符号,我们会对其进行反斜杠转义以防止 shell 处理它。

sed -i "s/.*USER.*/export USER=$signumid/
    s% DEVENVHOME=[$]%export DEVENVHOME=\${DEVENVHOME:-/home/$signumid/CPM_WORKAREA/devenv.x}%" .bashrc

等效地,您可以在脚本中不被 shell 触及的部分周围使用单引号,然后在需要插值的部分周围放置一个相邻的双引号字符串,例如

'un$touched*by$(the!shell)'"$signumid"'more$[complex]!stuff'

这个最终脚本仍然依赖于一些关于你真正想要什么的幸运或可能相当不幸的猜测。在第一行,我只将USER 更改为与整行匹配的正则表达式——也许这不是你想要的?另一方面,第二行做了相反的假设,只是为了让你可以看到变化——它只替换了我们匹配的实际文本。可能其中一个需要更改。

最后,请注意两个独立的sed 命令是如何合并到一个脚本中的。许多新手没有意识到sed 是一种脚本语言,它可以在脚本中接受任意数量的命令,而只是将其视为具有有趣语法的“替换”程序。

另一个常见的混淆来源是评估顺序。 shell 甚至在 sed 开始执行之前就处理双引号字符串,所以如果你在引用中有错误,你很容易在 sed 脚本中产生语法错误,这会导致相当无信息的错误消息(因为 @987654344 @ 在错误消息中告诉您基于 shell 替换后脚本的外观)。例如,如果signumid 包含斜杠,则会产生语法错误,因为sed 会将这些视为s/// 命令的终止分隔符。一个简单的解决方法是切换到signumid 的值中没有出现的分隔符。

【讨论】:

  • 这个答案的很多内容是在您最近的编辑之前编写的,但我希望它仍然有意义。
最近更新 更多