I have a script, that does nothing useful but execute the positional arguments. (I'm aware of the security risks, and the script does not make anything useful because it's a minimal working example.)
$ cat script
> #!/usr/bin/env bash
>
> eval "${*}"
$ cat "docu ment"
> Lorem ipsum dolor sit amet
What I would like to do is call the script with ./script cat "docu ment"
, or ./script cat docu\ ment
, but the quotes or the escape character vanishes and the script will try cat docu ment
, which doesn't work. How would I fix the quoting in such a case?
EDIT: What I really want to do, is invoke a command as many times until it returns a successful exit code, or it tried n times. My script looks like this:
#!/usr/bin/env bash
# Try command several times, until it reports success (exit code 0)
# or I give up (tried n times)
tryMax=10
try=1
# Do until loop in bash
while
eval "${@}"
exitcode="${?}"
[[ "${exitcode}" -ne 0 && "${try}" -lt "${tryMax}" ]]
do (( try++ ))
done
if [[ "${exitcode}" -ne 0 ]]; then
echo -n "I tried hard, but did not manage to make this work. The exit code "
echo "of the last iteration of this command was: ${exitcode}."
exit "${exitcode}"
fi
Best Answer
You don't need
eval
here. You can just use"$@"
:"$@"
will expand to all the arguments to the script as separate "words" - respecting the original quoting that prevented word splitting - and then leave you with the first argument as the command waiting to run (cat
), and the remaining arguments as arguments tocat
(docu ment
).Where this won't work:
"$@"
is expanded.! cmd
.!
is also processed at the start of handling a command, before parameter expansion.x \; y
or$'x\ny'
orx $'\n' y
, or the same with&&
or||
. All those are just regular arguments.LD_LIBRARY_PATH=/x foo
. You can put them before the script name, but not the argument command.>foo
,3<bar
in it. These may or may not be able to be affixed to the script, since the script has its own logging output.eval
anyway.( ... )
or command group{ ... ; }
. These will be treated as commands called(
and{
, not as syntactic constructs.$(...)
that needs to be run repeatedly. You can use that (or any other shell construct) to generate the original arguments, but they will all be fixed strings once the script starts running.$RANDOM
or an arithmetic expansion$((i++))
.time
. This is a shell reserved word, and not a built-in command, so it is also processed before parameter expansion.Otherwise, however, you can successfully avoid
eval
entirely, and should probably do so. It's very fragile to construct correctly even ignoring any possible security issues.