3

I would like to have a shell script to do a find and replace on anything without using regular expressions (regex), that includes all the specials chars such as !@#$%¨&*()?^~]}[{´`>.<,"'

So for example the function would be able to receive two parameters where the 1st will be what I want to find, and the second parameter will be what will be replaced with.

Example 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

Example 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^

The param1 and param2 could be really anything.

I need to use UNIX OS AIX it could be KornShell (ksh) using sed or awk. I can not use either perl or Java. I also can not install anything on this server.

javaPlease42
  • 4,699
  • 7
  • 36
  • 65
George
  • 171
  • 9
  • 1
    You could try an awk script with a function like the escape_pattern() suggested [here](http://backreference.org/2010/03/13/safely-escape-variables-in-awk/). Looks like you'd have to augment the function to include more chars to escape based on your list. You'd just have to wrap the parameters in calls to a function like that before performing a regular sub()/gsub(). – n0741337 Jan 06 '14 at 18:18

1 Answers1

8

I'd use awk and the string functions index() and substr() that don't use patterns

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

I assume that there may be more than one match per line.


testing

$ 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

The 2nd test is a bit problematic:

$ 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^]'

So you need to pre-process the parameters to escape any backslashes:

$ 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^]'

To accomodate Ed's astute point:

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
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 3
    You actually shouldn't use quite that approach as the `repl` string might contain the `find` string and then you'd be stuck in an infinite loop. You need to find every occurrence of `find` in the current record first and then replace them all with `repl`s. – Ed Morton Jan 06 '14 at 21:01
  • Nice partitioning of the record as you go instead of having to remember all of the matching locations. – Ed Morton Jan 07 '14 at 00:06
  • @glenn jackman, great piece of code! just one thing that didn't work here because I am using ksh. I cant perform find="$(sed 's/\\/&&/g' <<< "$param1")". It says ksh:syntax error: `<' unexpected. I believe using | echo is not an option here since I would get ride of spaces by doing that, etc Can you please advice? – George Jan 07 '14 at 13:14
  • @George, Do this then: `-v find="$(echo "$param1" | sed 's/\\/&&g/')"` -- there is no problem with echo and whitespace as long as you quote the variable. This is not a ksh problem per se, your ksh is just too old to understand the here-string redirection `<<<"string"` – glenn jackman Jan 07 '14 at 15:38