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.

1
  • 1
    You could try an awk script with a function like the escape_pattern() suggested here. 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(). Commented Jan 6, 2014 at 18:18

1 Answer 1

9

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
Sign up to request clarification or add additional context in comments.

4 Comments

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 repls.
Nice partitioning of the record as you go instead of having to remember all of the matching locations.
@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, 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"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.