【问题标题】:Quoting special characters with sed用 sed 引用特殊字符
【发布时间】:2013-12-06 15:12:50
【问题描述】:

我正在尝试查看传递给我的程序的变量(变量是 $1),并用所述特殊字符的引号形式替换任何特殊字符,以免特殊字符实际执行通常的操作.

我的代码是

#!/bin/sh
target="$1"
newtarget=`echo "$target" | sed -e s/\*/\\*/g`
newtarget=`echo "$newtarget" | sed -e s/\^/\\^/g`
newtarget=`echo "$newtarget" | sed -e s/\+/\\+/g`
newtarget=`echo "$newtarget" | sed -e s/\-/\\-/g`
newtarget=`echo "$newtarget" | sed -e s/\\/\\\/g`
newtarget=`echo "$newtarget" | sed -e s/\./\\./g`
newtarget=`echo "$newtarget" | sed -e s/\$/\\$/g`
newtarget=`echo "$newtarget" | sed -e s/\[/\\[/g`
newtarget=`echo "$newtarget" | sed -e s/\]/\\]/g`
sed s/"$newtarget"/"$2"/g "$3" > "$3.updated"
mv "$3.updated" $3

我的第一行,$target,应该查看目标字符串,看看字符串中是否有 *。如果有,它将用 * 替换它。在代码中,它出现为 * 然后是 \* 的原因是程序看不到 * 并认为它想实际使用 *,它只是将 * 看作是一个常规字符,用 .我在所有其他行中都做了同样的事情,但角色不同。在第一个之后,它应该签入 newtarget 并执行相同的操作,但使用不同的字符。

我的整个程序应该做的是,它传递了3个参数,第一个是要替换的字符串,第二个是要替换的字符串,第三个是文件名。所以到最后,如果文件最初是这样的

aa\^a*aa$aa[aaa$a]a 

我提供

"a\^a*" "test"

作为参数,结果应该是

atestaa$aa[aaa$a]a 

但我的代码仍然无法正常工作。我的代码有什么问题?我不知道我的 sed 语法是否适合编码,或者我的附加语句是否不起作用,或者我是否必须对某些特殊字符进行特殊引用。

编辑:我知道我应该能够像我一样使用多个 sed 命令来做到这一点,但我不知道为什么它们不能正常工作,所以我很确定这与我的引用有关在“newtarget=”行末尾的实际 sed 命令中。

EDIT2:我现在在我的代码中引用了我的 sed 参数,但它仍然无法正常工作。我需要引用某些特殊字符的特殊方法吗?我认为在每个字符前面加上反斜杠会正确引用它。

#!/bin/sh
target="$1"
newtarget=`echo "$target" | sed -e 's/\*/\\*/g'`
newtarget=`echo "$newtarget" | sed -e 's/\^/\\^/g'`
newtarget=`echo "$newtarget" | sed -e 's/\+/\\+/g'`
newtarget=`echo "$newtarget" | sed -e 's/\-/\\-/g'`
newtarget=`echo "$newtarget" | sed -e 's/\\/\\\/g'`
newtarget=`echo "$newtarget" | sed -e 's/\./\\./g'`
newtarget=`echo "$newtarget" | sed -e 's/\$/\\$/g'`
newtarget=`echo "$newtarget" | sed -e 's/\[/\\[/g'`
newtarget=`echo "$newtarget" | sed -e 's/\]/\\]/g'`
sed s/"$newtarget"/"$2"/g "$3" > "$3.updated"
mv "$3.updated" $3

【问题讨论】:

  • 将参数引用到sed -e。顺便说一句,您也可以将它们合并为一个:sed =e 's/\*/\\&/g' -e 's/\^/\\&/g' … 甚至sed -e 's/[][*^+\\.$-]/\\&/g'。您还需要转义斜杠(分隔符)。
  • 我尝试了你给我的最后一段代码,但我无法让它工作,所以我回到我的原始代码,只是引用了你说的 sed -e 参数,但我仍然收到错误消息。当您说我需要转义斜杠分隔符时,您是指在我的所有行中,还是在我有很多行的行中('s/\\/\\\/g')

标签: unix sed


【解决方案1】:

多次调用sed 的目的是在每次出现一组字符之前放置一个文字反溅。这可以通过一次调用 sed 来完成,但您需要注意如何指定集合。

首先,让我们看看通用命令是什么样子的:

newtarget=$( echo "$target" | sed -e 's/\([...]\)/\\\1/g'

其中... 将被替换为要转义的字符集。此命令使用括号来捕获其中一个字符的单个实例,并将其替换为反斜杠,后跟捕获的字符。要指定字符集,请使用

[]*^+\.$[-]

两个注意事项:首先,] 必须先出现,以免被误认为是集合的结尾,因为 [] 是无效集合。其次,- 必须放在最后,以免误认为是范围运算符(例如,[a-z] 是小写字母的集合,而 [az-] 只是三个字符 az、和-)。

把它们放在一起:

 newtarget=$( echo "$target" | sed -e 's/\([]*^+\.$[-]\)/\\\1/g' )

【讨论】:

  • 我尝试将其实现为newtarget=echo "$target" | sed -e 's/(]*^+\.$[-)/\\\1/g'`` 但我得到一个未终止的 `s' 命令的错误,以及“没有以前的正则表达式”。我以为你的意思是 [...] 只是为了象征我应该把集合放在那里,而不是我应该把它放在括号内(因为如果我把 ] 首先放在开头,我们就会有一个 [] .
  • 不,周围的[...] 是必要的,因为它指定了一个匹配它们包含的字符之一的正则表达式。请参阅我关于空集 [] 无效的评论,因此将 ] 首先放置允许它按字面意思解释,而不是作为集合的右括号。
  • 嗯,好吧。如果我想在集合中加入另一个特殊角色,我会怎么做?例如,如果我想包含 ` ,我会在 + 和 ^ 之间插入它,还是需要一些特殊的东西,比如括号和 -?
  • 我的命令现在看起来像 newtarget=echo "$target" | sed -e 's/\([]*^+\.$[-]\)/\\\1/g',但我收到错误“无效的反向引用”。关于任何角色,我应该注意什么特殊要求吗?此外,echo 中的 e 之前和 g 之后的逗号之后有 `,但注释的格式很时髦
  • 我认为不同版本的sed 对什么是转义和不转义有不同的规则。尝试删除括号前的反斜杠,看看是否有帮助。
【解决方案2】:

你所做的问题是你没有引用你的sed 表达式。比如写

sed s/\*/\\*/

作为

sed 's/\*/\\*/'

sed s/\*/\\\\*/

我不确定你为什么需要那个复杂的函数来转义特殊字符。您可以定义一个函数来返回转义的输入字符串:

myescape() { printf "%q" "$1"; }

%q

使printf 以一种格式输出相应的参数 可以作为shell输入复用。

另一个将参数传递给sed的函数:

myreplace() { sed "s/$1/$2/" <<< "$3"; }

现在你可以通过说来调用它:

myreplace "$(myescape 'pattern')" "replacement" "original_string"

例子:

$ myescape() { printf "%q" "$1"; }
$ myreplace() { sed "s/$1/$2/" <<< "$3"; }
$ myreplace $(myescape 'a\^a*') 'test' 'aa\^a*aa[aaa]a'
atestaa[aaa]a

【讨论】:

  • 然而,我们不应该这样做。我应该使用 sed 命令以这种方式执行此操作,并且我想了解为什么我的代码不起作用。
  • @JoJoBya 您应该在问题中明确说明这是家庭作业,并且您不是在寻找解决问题的方法,而是希望人们修复您的代码
  • 那个 printf %q 无论如何都是 GNU bash 特有的,他需要转义 sed 特殊字符,而不是 shell 特殊字符。
  • @JoJoBya 您的脚本的问题是您没有引用sed 表达式。特定问题还有其他问题。
猜你喜欢
  • 1970-01-01
  • 2019-09-07
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
相关资源
最近更新 更多