I have a list that looks like this:


I would like to replace any occurence of a dot in the username part. So that:




I tried it with sed but couldnt get a Regex together to only apply the change to the username part. Do you have an idea for a pattern to do this?


If there's more than one dot, but only one dot after the @ sign (which would be usual), you can just change all dots and change the last one back:

sed 'y/./^/;s/\(@.*\)\^/\1./'

If there's potentially more than one dot both before and after the @ sign, you can do it recursively with a short loop:

sed ':top;s/\.\(.*@\)/^\1/;ttop'

This works in GNU sed; BSD sed requires a newline after a label:

sed ':top 

EDIT: To handle all cases in either GNU sed or BSD sed in a single line command:

sed 'h;s/@.*//;y/./^/;G;s/\n.*@/@/'

h copies current line (called the "pattern space") to the hold space; the s command deletes the @ and everything after it; y works like the shell command tr and in this case translates all dots to carets; G appends a newline and the hold space contents to the pattern space; then the last s command deletes from the newline up to the @ and puts back the @.

  • Thanks a lot! Exactly what I wanted since it might be, that the domain part contains more dots – valh Oct 27 '15 at 9:21

Use awk:

awk -F"@" '{gsub("\.","^",$1)}OFS="@"' file
  • -F"@" delimits the input with @.
  • gsub() replaces all dots with ^ in the first field $1 (the name part before the @).
  • OFS sets the output field separator.
  • Interesting trick with the OFS="@"... I couldn't figure out how that worked at first! I would tend to write it as: awk -F"@" -v OFS="@" '{gsub("\.","^",$1);print}' file – Brian Oct 27 '15 at 13:41
  • @Brian This is just a shortcut. Inside the brackets {} awk changes something and then we need a true condition that awk prints the line. You could also write {}1 where 1 is the true condition, but I anyway have to set the OFS variable so I used that as true condition. – chaos Oct 27 '15 at 13:44
  • Interesting...I always set OFS and FS either on the command line or in a BEGIN block; I feel it's cleaner. There seems to be a mistake with the "\." pattern; on my machine it replaces ALL chars in $1. Seems you need either "\\." or /\./. Adding one to the return value of gsub is a sufficient "true" test: awk -F'@' -v OFS='@' 'gsub(/\./,"^",$1) + 1' – Wildcard Nov 27 '15 at 7:57
  • @Wildcard You're using gawk. This answer was written using mawk. With gawk, you have to add slashes. And that statement OFS="@". That was my inner code golfer =) It's less clear, but more elegant... – chaos Nov 27 '15 at 8:10

If you only have a single dot, you can use

sed 's/\.\(.*\)@/^\1@/'

which matches the dot, then captures the rest until the @, and replaces it by ^, the capture, and @.


This is how I would do it with sed.

sed ':start s/^\([^@]*\)\./\1^/; t start;' "$@"

:start is a label to branch to.

^\([^@]*\)\. is a regular expression (aka regex), that matches, at the beginning of line, 0 or more of any character except the at-sign, followed by a dot. The \( and \) signify a back reference which means anything which matches the enclosed pattern can be referenced in the replacement side with \1.

\1^ is the replacement. \1 is replaced by whatever matched \([^@]*\) in the regular expression. The circumflex (^) will replace the dot which notice was excluded from the back reference.

t is a sed test which will branch to the given label if the previous subsitution was successful. This enables the script to replace more than one dot in the email address.

  • +1 for good explanation. This wasn't here when I started writing my answer. :) See note in my answer about labels and newlines in BSD sed, though. (Mac OS X uses BSD sed.) – Wildcard Oct 27 '15 at 8:42

A system which has sed and awk will also often have perl, and if you're open to a perl solution, try

perl -ne '($user, $domain) = split "@"; $user =~ s/\./^/g; print "$user\@$domain"'

As long as your email addresses are fairly normal and each line contains one address (and nothing else), this works in the straightforward way: split the address on @, replace all periods in the first part with carets, and print out the two parts together as an email address.

You could also get tricky and do this:

perl -pe 's/\.(?=.*@)/^/g'

which uses a regex to replace any period which comes before an @ symbol with a caret. It relies on a lookahead assertion, a feature which is not implemented in sed's or awk's regular expression flavors, as far as I know. I'd guess this may be slower for long lists, but run your own tests and see if it matters.

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.