The obvious way to do this has many problems:
# This will fail with certain inputs. HTML will certainly be a problem
# as the '<' and '>' characters will be interpreted as file redirects
$ while read r; do eval echo $ r; done <input
The following perl class should correctly handle the problem for simple inputs.
$ perl -pwe 'while (($ k, $ v) = each% ENV) {s / \ $ {? $ k}? / $ v /}' input
But it does nothing with constructs like $ {FOO-bar}. If you need to handle such constructions, it may be enough to avoid all shell metacharacters and execute a while / read loop:
$ sed -e 's / \ ([<> & | ();] \) / \\\ 1 / g' input | while read -rl; do eval echo "$ l"; done
Please note that this is not reliable and safe. Consider what happens when you type, for example:
\; rm -rf /
I said "consider." Do not check this. Sed will insert a backslash before the semicolon, then eval will get the string "\\;" which will be interpreted as a single backslash followed by a semicolon, which includes an echo, and rm -rf is executed. Given the uncertainty of avoiding unknown input, it would probably be safer to stick with something like perl and explicitly replace the required sh-constructs. Sort of:
$ perl -pwe 'while (($ k, $ v) = each% ENV) {s / \ $ {? $ k}? / $ v /};
s / \ $ {[^ -] * - ([^}] *)} / $ 1 / g 'input
This has problems with input like $ {FOO = some-text}. To reliably get all sh constructions ($ {word: rhs}, where ":" can be any of "-", "?", "=", "+", "%", "#" Or any of them with by adding a colon (or a lot of other characters if you allow non-visit syntax)!) you will need to build a rather complicated set of comparisons.