2

I need to replace _ (underscore) with ? (question mark) in a file containing email addresses.

The file looks like below:

EFT_020034-E015133  20140624    /ACC/EMAIL+test_1@mysite.com         SHR    END 
EFT_020034-E015133  20140624    /ACC/EMAIL+test_123_abc@yoursite.net     SHR    END
EFT_020034-E015133  20140624    /ACC/EMAIL+test_456@theirsite.com        SHR    END

The expected output is:

EFT_020034-E015133  20140624    /ACC/EMAIL+test?1@mysite.com         SHR    END 
EFT_020034-E015133  20140624    /ACC/EMAIL+test?123?abc@yoursite.net     SHR    END
EFT_020034-E015133  20140624    /ACC/EMAIL+test?456@theirsite.com        SHR    END

How can I do this in sed or awk without affecting other underscores and only the underscore between EMAIL+ (constant) and SHR (constant). The changed content should be saved in a new file.

4

awk will do this:

$ awk '{ gsub("_", "?", $3) ; print }' < data
EFT_020034-E015133 20140624 /ACC/EMAIL+test?1@mysite.com SHR END
EFT_020034-E015133 20140624 /ACC/EMAIL+test?123?abc@yoursite.net SHR END
EFT_020034-E015133 20140624 /ACC/EMAIL+test?456@theirsite.com SHR END

The email addresses are in field 3, so we replace _ with ? in field 3 only, including when there is more than one _, using gsub.

  • 2
    Note that is replaces sequences of blanks with a single space character and removes the leading and trailing blanks. – Stéphane Chazelas Jun 25 '14 at 10:31
  • Thank you Michael. It works on my example file. However, when I test it in actual file where the email is located at field 20 or above, nothing happens on the result. Also, I test it using field 1 to replace EFT_ with EFT?, it only works on the first line. Is it due to unix/dos line terminator? Thanks – user73549 Jun 25 '14 at 10:37
  • @user73549: Line terminators could cause the second part. You can set the RS variable inside the awk script to change the record (line) separator. awk keeps numbering the records indefinitely, so I don't know about the other part. I suspect that your idea of which field the address is in doesn't match awk's - you can set the field separator with the -F command-line option, or try replacing the field value entirely with $23 = "X" to see what it thinks. – Michael Homer Jun 26 '14 at 5:45
  • Yes, thank you. I see that line terminator causes my script to misbehave in my subject text file. Thank you everyone. I learned a lot. – user73549 Jun 26 '14 at 12:07
  • to add, the text file has CRLF and LF when I view in viewer including the whitespaces and terminator. – user73549 Jun 26 '14 at 12:10
3

With sed you could do:

sed -e :1 -e 's/_\([^+]*@\)/?\1/;t1'

That is replace _ followed by a sequence of non-+ characters followed by @ with ? with the sequence of character, and repeat the process as long as it matches.

Or to do it only between EMAIL+ and SHR:

sed -e :1 -e 's/\(EMAIL+.*\)_\(.*SHR\)/\1?\2/;t1'

If you want to only consider the lines that start with ^EFT, you can add a -e '/^EFT/!b' to leave alone those that don't like:

sed -e '/^EFT/!b' -e :1 -e 's/\(EMAIL+.*\)_\(.*SHR\)/\1?\2/;t1'

Note that for an input like:

EFT EMAIL+ foo_bar SHR bar_baz EMAIL+ SHR

Both underscores will be replaced because they're both between an EMAIL+ and a SHR.

To avoid that, you could do something like:

sed '
  /^EFT/!b # leave the non-EFT lines alone (branch out)
  s/%/%p/g; s/</%l/g; s/>/%r/g; # escape the <>% characters with %
  s/EMAIL+/</g; s/SHR/>/g; # replace EMAIL+ and SHR with < and >
  :1
  s/\(<[^<>]*\)_\([^<>]*>\)/\1?\2/; t1
  s/</EMAIL+/g; s/>/SHR/g; # restore EMAIL+ and SHR
  s/%r/>/g; s/%l/</g; s/%p/%/g; # restore the escaped <>%'
  • I also added ^EFT to specify the beginning of line at search field - sed -e :1 -e 's/(^EFT.*EMAIL+.*)_(.*SHR)/\1?\2/;t1' – user73549 Jun 25 '14 at 12:16
  • Thank you so much. I actually use your second script and it solve my problem. – user73549 Jun 26 '14 at 12:09
1
sed '/.*EMAIL+\(.*\)SHR.*/{
    h;s//\1/;y/_/?/;G
    s/\(.*\)\n\(.*EMAIL+\).*SHR/\2\1SHR/}'

That should do the job pretty reliably - it will replace all _ with ? between the last EMAIL+ occurring on a line and the last SHR to occur on same and only on those lines containing both strings.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.