【问题标题】:Replace string without regex替换没有正则表达式的字符串
【发布时间】:2014-01-24 05:38:56
【问题描述】:

我想要一个 shell 脚本在不使用正则表达式 (regex) 的情况下对任何内容进行查找和替换,其中包括所有特殊字符,例如 !@#$%¨&*()?^~]} [{´`>.

因此,例如,该函数将能够接收两个参数,其中第一个参数是我要查找的参数,第二个参数是要替换的参数。

示例 1:

File1:
BETWEEN TO_DATE('&1','YYYY-MM-DD') +1  and TO_DATE('&2','YYYY-MM-DD') +15

First parameter: TO_DATE('&1','YYYY-MM-DD') 
Second parameter: TO_DATE('&1 23:59:59','YYYY-MM-DD HH@$:MI:SS') 

File1 after calling the function to replace:

BETWEEN TO_DATE('&1 23:59:59','YYYY-MM-DD HH@$:MI:SS')  +1  and TO_DATE('&2','YYYY-MM-DD') +15

示例 2:

File2:
This is just a test/2^

Paramter1: test/2^
Paramter1: testing\$2^]'

File2 after calling the function to replace:
This is just a test/2^

param1 和 param2 可以是任何东西。

我需要使用 UNIX 操作系统 AIX 它可能是使用 sed 或 awk 的 KornShell (ksh)。我不能使用 perl 或 Java。我也无法在此服务器上安装任何东西。

【问题讨论】:

  • 您可以尝试使用类似 escape_pattern() 建议 here 的函数的 awk 脚本。看起来您必须根据您的列表增加功能以包含更多要转义的字符。在执行常规 sub()/gsub() 之前,您只需要将参数包装在对类似函数的调用中。

标签: shell unix scripting sed awk


【解决方案1】:

我会使用 awk 以及不使用模式的字符串函数 index() 和 substr()

awk -v find="$param1" -v repl="$param2" '{
    while (i=index($0,find)) 
        $0 = substr($0,1,i-1) repl substr($0,i+length(find))
    print
}' file

我假设每行可能有多个匹配项。


测试

$ param1="TO_DATE('&1','YYYY-MM-DD')"
$ param2="TO_DATE('&1 23:59:59','YYYY-MM-DD HH@$:MI:SS')"
$ awk -v find="$param1" -v repl="$param2" '{
    while (i=index($0,find)) 
        $0 = substr($0,1,i-1) repl substr($0,i+length(find))
    print
}' << END
BETWEEN TO_DATE('&1','YYYY-MM-DD') +1  and TO_DATE('&2','YYYY-MM-DD') +15
END
BETWEEN TO_DATE('&1 23:59:59','YYYY-MM-DD HH@$:MI:SS') +1  and TO_DATE('&2','YYYY-MM-DD') +15

第二次测试有点问题:

$ param1='test/2^'
$ param2='testing\$2^]'\'
$ awk -v find="$param1" -v repl="$param2" '{
    while (i=index($0,find)) 
        $0 = substr($0,1,i-1) repl substr($0,i+length(find))
    print
}' <<< "This is just a test/2^"
awk: warning: escape sequence `\$' treated as plain `$'
This is just a testing$2^]'

因此您需要对参数进行预处理以转义任何反斜杠:

$ awk -v find="$(sed 's/\\/&&/g' <<< "$param1")" \
      -v repl="$(sed 's/\\/&&/g' <<< "$param2")" \
'{
    while (i=index($0,find)) 
        $0 = substr($0,1,i-1) repl substr($0,i+length(find))
    print
}' <<< "This is just a test/2^"
This is just a testing\$2^]'

为了适应 Ed 的精明观点:

param1=foo
param2=_foo_
awk -v find="$(sed 's/\\/&&/g' <<< "$param1")" \
    -v repl="$(sed 's/\\/&&/g' <<< "$param2")" \
'
    index($0, find) {
        start = 1
        line = $0
        newline = ""
        while (i=index(line, find)) {
            newline = newline substr(line, 1, i-1) repl
            start += i + length(find) - 1
            line = substr($0, start)
        }
        $0 = newline line
    }
    {print}
' <<END
abcfoodeffooghifoobar
food
blahfoo
no match
END
abc_foo_def_foo_ghi_foo_bar
_foo_d
blah_foo_
no match

【讨论】:

  • 您实际上不应该使用这种方法,因为repl 字符串可能包含find 字符串,然后您将陷入无限循环。您需要先找到当前记录中出现的所有find,然后将它们全部替换为repls。
  • 对记录进行很好的分区,而不必记住所有匹配的位置。
  • @glenn jackman,一段很棒的代码!只有一件事在这里不起作用,因为我使用的是 ksh。我无法执行 find="$(sed 's/\\/&&/g'
  • @George,然后执行此操作:-v find="$(echo "$param1" | sed 's/\\/&amp;&amp;g/')" -- 只要引用变量,回显和空格就没有问题。这本身不是 ksh 问题,您的 ksh 太旧了,无法理解此处的字符串重定向 &lt;&lt;&lt;"string"
猜你喜欢
  • 2017-07-29
  • 2018-07-13
  • 2017-02-04
  • 1970-01-01
  • 1970-01-01
  • 2015-11-30
  • 2021-11-29
相关资源
最近更新 更多