davidrw's solution is the best imho, with some minor changes to the substitution line:
$s =~ s/\$([a-zA-Z]\w*)/exists $data{lc $1} ? $data{lc $1} : ''/esg;
There's no point allowing just uppercase letters and then lowercasing them. It's also not a good idea (at least in my opinion) to leave $vars that don't have a value defined as the original text. The intention was probably to keep things like $10.45 from being interpreted as '', but you can easily avoid this by defining your var names as starting with a letter.