6
$ echo "104_Fri" | sed 's/^\([0-9]+\)_\([A-Za-z]+\)$/\1;\2/'
104_Fri

I would like to match the digits at the beginning and the letters at the end - each as a group. Afterwards I want to output the first group, a semicolon and then the second group.

I would expect this expression to yield:

104;Fri

Why does this not work?

5 Answers 5

11

Backslash the +:

$ echo "104_Fri" | sed 's/^\([0-9]\+\)_\([A-Za-z]\+\)$/\1;\2/'
104;Fri

Note that + is not a standard basic regular expression metacharacter, and so doesn't have portable behaviour in sed even when backslashed.

You should use sed -r or sed -E to enable extended regular expressions instead, in which you don't need to backslash any of these characters. These options are also non-standard, but at least you'll get an error if they're unsupported rather than mysterious failures. The options are supported by GNU sed and all the main BSD derivatives (FreeBSD, OpenBSD, NetBSD, OS X), but not in many of the commercial Unix versions.

If you need extended regular expressions truly portably, use awk, which always uses them.

3
  • 2
    Of course, x+ is easily rewritten as xx* if the portable subset of sed must be used.
    – hvd
    Jul 28, 2014 at 13:21
  • TIL why ab+ doesn't work in vim while ab* does!
    – Kevin G.
    Jul 28, 2014 at 15:04
  • If everyone used PCRE (or a subset of it, such as POSIX ERE), the world would be a happier place.
    – nyuszika7h
    Jul 28, 2014 at 15:14
7

You must escape plus symbol +, too:

$ echo "104_Fri" | sed 's/^\([0-9]\+\)_\([A-Za-z]\+\)$/\1;\2/'
104;Fri
6

Add the -r option ;-) for extended regexps, and the need to \ active content diminishes.

$ echo "104_Fri" | sed -re 's/^([0-9]+)_([A-Za-z]+)$/\1;\2/'
104;Fri

As the Q is written, (no background data) the splitting-task could be done in several other simpler ways:

$ echo "104_Fri" | tr '_' ';'
104;Fri

$ echo "104_Fri" | sed  's/_/;/'
104;Fri

... to name two.

5

You might also alternate addresses. You wind up using far fewer crazy backslashes that way.

sed -n '/._./{/^[0-9]*.[A-Za-z]*$/s/_/;/p;}'

sed -n '/[^0-9].*_.*[^A-Za-z]/d;/._./s/_/;/p'
1
  • 1
    +1 for the ._. smiley.
    – nyuszika7h
    Jul 28, 2014 at 15:08
1

I suggest using Perl instead, as it comes preinstalled on most Unix systems:

$ echo '104_Fri' | perl -p -e 's/^([0-9]+)_([A-Za-z]+)$/$1;$2/'

You can simplify that further:

$ echo '104_Fri' | perl -p -e 's/^(\d+)_([a-z]+)$/$1;$2/i'

Note: If your input is really as simple as in the question, a simple tr will do:

$ echo '104_Fri' | tr '_' ';'

Or with Perl:

$ echo '104_Fri' | perl -p -e 's/_/;/'

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.