使用任何东西作为sed或Perl正则表达式的输入?

San*_*ing 1 linux bash perl sed

如果我做

comment () { sed -i "/$1/s/^/$2 /g" $3 }

comment /dev/sysmsg '#' /tmp/1
comment '*.err'     '#' /tmp/1
Run Code Online (Sandbox Code Playgroud)

带输入文件

*.err;kern.notice;auth.notice   /dev/sysmsg
fff
ff
Run Code Online (Sandbox Code Playgroud)

然后它就像/sed分隔符中那样断开,*也被视为正则表达式.

有没有办法使它健壮,所以输入字符串$1可以包含我想要的任何东西?或者我需要转移到Perl?

gle*_*man 5

当然,使用bash参数替换来逃避麻烦的斜线字符:

comment () { sed -i "/${1//\//\\/}/ s/^/${2//\//\\/} /" "$3"; }
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 您需要保护图案和更换部件中的斜线.
  • 如果你的搜索被锚定,使用"g"是没有意义的,因为模式最多可以匹配一次.
  • 引用所有变量:如果文件名包含空格,则代码会中断.
  • 一行函数在闭括号之前需要一个分号.

演示

$ cat file
test/1/
test/2/
test/3/

$ comment 'test/2' '//' file

$ cat file
test/1/
// test/2/
test/3/
Run Code Online (Sandbox Code Playgroud)

我意识到我并没有逃避正则表达式的特殊字符.最安全的方法是转义任何非字母数字字符:

comment () { 
    local pattern=$(sed 's/[^[:alnum:]]/\\&/g' <<<"$1")
    local replace=${2//\//\\/}
    local file=$3
    sed -i "/$pattern/ s/^/$replace /" "$file"
}
Run Code Online (Sandbox Code Playgroud)

但既然你想做纯文本匹配,sed可能不是最好的工具:

comment() { 
    perl -i -pse '$_ = "$leader $_" if index($_, $search) > -1' -- \
        -search="$1" \
        -leader="$2" \
        "$3"
}
Run Code Online (Sandbox Code Playgroud)