I had a legitimate need for eval
-ing a string: a helper scripts runs a complicated sequence of pipes with double-quoting (e.g. --json-override='"key": "value"'
), 460+ characters long. But when I run it with --noop
, I want to see the command that would be executed, in a copy-pasteable form for manual execution. I came up with this:
echo_eval () {
cmd=$(cat)
if ((noop)); then
echo "$cmd"
else
eval "$cmd"
fi
}
foo () { for arg; do echo "[$arg]"; done; }
echo_eval <<EOF
foo 1 2 "3 4" '"5 6"' " 7 " "\"8 9\"" | tac
EOF
exit 1
The main purpose of the here-document is to skip all the pitfalls of using the ' and " quotes in an already ' or "-quoted string (pain and suffering).
While I haven't tested it extensively, it seems to hit the relevant check-boxes, like pipes, quoting, quoting space-containing arguments, preserving quoted quotes, escaped quotes, leading/trailing space in arguments, etc.:
["8 9"]
[ 7 ]
["5 6"]
[3 4]
[2]
[1]
When noop
is true, I get:
foo 1 "2" "3 4" '"5 6"' " 7 " "\"8 9\"" | tac
which is the command verbatim, copy-pasteable for manual execution and yielding the same output.
We can also use <<'EOF'
to disable substitution of any $
s before echo_eval
, but if any variable/command substitution does need to happen before calling echo_eval
, we have to use <<EOF
and escape all $
s that shouldn't be evaluated before.
Having said all this, I'd gladly (a) hear if there are pitfalls (apart from the $
mentioned above) and (b) see a better solution to the problem if there is one.