I have a string similar to ../../sdd1 and trying to match sdd from here. I'm trying to use the following sed command:

echo "../../sdd1" | sed 's:.*\([a-z]\{3\}\)[0-9]?:\1:' 

and it does not produce a match.
But when I use

echo "../../sdd1" | sed 's:.*\([a-z]\{3\}\)[0-9]\{0,1\}:\1:'

then I get my match, sdd.

My best guess is that I should escape the ? as well, similar to the curly brackets - I tried it and it works, but I don't know why.

So the question is, why does the [0-9]? regex not match the 1 from sdd1, or why do we have to escape the ? and the curly brackets?


By default, sed uses BRE and would need -E or -r option to use ERE

Quoting from GNU sed manual

In GNU sed the only difference between basic and extended regular expressions is in the behavior of a few special characters: ‘?’, ‘+’, parentheses, braces (‘{}’), and ‘|’.

With basic (BRE) syntax, these characters do not have special meaning unless prefixed backslash (‘\’); While with extended (ERE) syntax it is reversed: these characters are special unless they are prefixed with backslash (‘\’).

so, for GNU sed, use

$ echo '../../sdd1' | sed 's:.*\([a-z]\{3\}\)[0-9]\?:\1:'
$ echo '../../sdd1' | sed -E 's:.*([a-z]{3})[0-9]?:\1:'

for POSIX BRE, your second command is the way to go

$ echo '../../sdd1' | sed 's:.*\([a-z]\{3\}\)[0-9]\{0,1\}:\1:'

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