Você pode primeiro escapar do & quando encontrado em uma entidade e, em seguida, substituir os restantes. Como:
LC_ALL=C sed 's/_/_u/g; # use _ as an escape character. Here escape itself
s/&\([[:alpha:]][[:alnum:]]*;\)/_a/g; # replace & with _a when in entities
s/&\(#[0-9]\{1,8\};\)/_a/g; # Ӓ case
s/&\(#x[0-9a-fA-F]\{1,8\};\)/_a/g; # ꯍ case
s/&/\&/g; # now convert the non-escaped &s
s/_a/\&/g;s/_u/_/g; # restore escaped & and _'
com perl :
perl -pe 's/&(?!#?\w{1,31};)/&/g'
Esse é um pouco mais lax do que o sed em que consideraria como entidade XML qualquer coisa que comece com & , um opcional # e qualquer número (até 31) de alnums (ou sublinhados ) e ; , enquanto o sed seria mais explícito no que uma entidade é (como em &#blah; não seria considerado como uma entidade). Na prática, não é provável que faça muita diferença.