The following, which replaces textual user and group names with UIDs and GIDs, works correctly:

echo "uidNumber=root" | sed -E 's/((u|g)idNumber=)([^,+]+)/echo -n \1; id -\2 \3/eg'

However, if text is added before the text matched by the pattern, it is prepended to the command executed in the substitution and causes a failure:

echo "anotherAttr,uidNumber=root" | sed -E 's/((u|g)idNumber=)([^,+]+)/echo -n \1; id -\2 \3/eg'
h: line 1: anotherAttr,echo: command not found

The same seems to happen if text is added after the text matched by the pattern, in which case it is appended:

echo "anotherAttr1,uidNumber=root,anotherAttr2" | sed -E 's/((u|g)idNumber=)([^,+]+)/echo -n \1; id -\2 \3/eg'
sh: line 1: anotherAttr1,echo: command not found
id: ‘root,anotherAttr2’: no such user

Why is non-matching text appended and prepended to the executed command, and how can I stop this happening?

Edit: Other users have suggested matching the entire line, but this prevents the g modifier from functioning correctly, and is not an acceptable solution.

  • sed substitutes for the matched portion of the line. If you don't match the entire line, the parts not matched remain outside the substitution.
    – pell
    Jul 11, 2023 at 18:46
  • Exactly, so why are they being executed as part of the command? Jul 11, 2023 at 18:47
  • Just to make sure we're understanding each other, do you get that (matched part)xyz will return (substituted part)xyz because xyz was not matched and so will remain in the output?
    – pell
    Jul 11, 2023 at 18:50
  • Match the entire line and substitute the parts you want to keep.
    – pell
    Jul 11, 2023 at 18:53
  • I'm aware that unmatched text will remain in the output, that's not the issue. The issue is the command in the substitution has non matching text prepended and appended to it before it's executed, and so fails. Matching the entire line would prevent the g modifier from working. Jul 11, 2023 at 18:56

1 Answer 1


This issue was due to a fundamental misunderstanding of how the e modifier works.

The s substitution is applied, either once, or across the whole pattern buffer (in the case of g). e then executes the contents of the pattern buffer after the substitutions have been performed.

The fix is fairly simple, escape dollars and slashes, prepend echo -n " to the pattern buffer, append " to the pattern buffer, and place all per-substitution commands in subshell expansions.

Adding p helps debugging by showing us what will be executed:

echo "anotherAttr1,uidNumber=root,gidNumber=root,anotherAttr2" | sed -E 's/\\/\\\\/g;s/\$/\\$/g;s/^/echo "/;s/$/"/;s/((u|g)idNumber=)([^,+]+)/\1$(id -\2 \3)/peg'
echo "anotherAttr1,uidNumber=$(id -u root),gidNumber=$(id -g root),anotherAttr2"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .