* means zero-or-more matches, and it matches as soon as possible. If you run that command without the
g flag (which means
sed will stop after the first replacement), you will get as output
habc 123. This is because it start reading from left to right, and because it couldn't match
a, it will simply match the beginning of the line and then stop there.
Using the global (
g) flag, it wil keep trying to match the rest of the string, and because
* matches the empty string when it can't match anything else, it will place an
h every time it cannot match more numbers.
Note that your second attempt is equivalent to
sed "s/[0-9]\+/h/". Here
+ means one or more matches, meaning it won't match the empty string when it does not find a number to replace.