I want to pass in multiple commands using a &&
operator into a single function call. Each command could have a different number of parameters so I'm using the $@
to capture all of them. With a single command, the function below works fine.
function try_log () {
$@ \
&& echo "PASS!"
|| echo "FAIL!"
}
$ try_log touch foo bar
PASS!
$ try_log rm foo bar
PASS!
$ try_log rm foo bar
rm: cannot remove 'foo': No such file or directory
rm: cannot remove 'bar': No such file or directory
FAIL!
I now want to add support for an &&
operatoron the input command. The intent is to run the entire command then display a PASS or FAIL at the end. I've tried the following without success.
try_log ls && ls # runs the send ls after the first completes
foo bar
PASS!
foo bar
try_log 'ls && ls'
ls: cannot access '&&': No such file or directory
ls: cannot access 'ls': No such file or directory
FAIL!
$ try_log `ls && ls`
foo: command not found
FAIL!
CodePudding user response:
First, your function does not work fine. You need to quote $@
to handle arbitrary arguments, and you should just use a regular if
statement rather than trying to simulate a conditional expression using &&
and ||
.
try_log () {
if "$@"; then echo "$PASS!"; else echo "FAIL!"; fi
}
The problem is that this only handles simple commands, consisting of the name of a command and its arguments. It does not handle pipelines, compound commands like [[
, if
, etc, or command lists like a && b
. The only real solution is to pass the command as a single string and let try_log
use eval
to parse and execute it.
try_log () {
if eval "$1"; then echo "$PASS!"; else echo "FAIL!"; fi
}
try_log "ls && ls"
(This is not an "evil" use of eval
, because you are using it exactly what it is intended for: executing arbitrary code. Just make sure you, and not an untrusted user, are providing the arbitrary code.)