help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Running commands as "$@"


From: Greg Wooledge
Subject: Re: Running commands as "$@"
Date: Thu, 2 Feb 2023 10:48:20 -0500

On Thu, Feb 02, 2023 at 12:11:32PM +0100, Cristian Zoicas wrote:
> Let's assume the following script.
> 
> # ---- begin script runner.sh ----
> eval "$@"
> # ---- end script runner.sh ----

First of all, this isn't a valid script because it has no shebang.

Second, that's not how eval is meant to be used.  If you're passing
literal shell code (sh or bash) that needs to be eval-ed by the
recipient, then you *don't* want to use "$@" which presents the code
as a series of separate arguments.

You either pass the entire script as *one* argument, in which case
you want   eval "$1"

or else you pass the script as a series of arguments that need to be
pasted together into a single string before eval, in which case
you want   evsl "$*"

It's all about your intent and your API for this script.

> If we call this script with the following command line
> 
>     $ sh runner.sh ls -l

Here, you're definitely not doing the "whole script in one argument"
routine, so it must be the "paste the arguments together into one string
and then parse it" routine.

eval "$*" is what you want here, if indeed you really with to pursue
this approach.

> Now, if a call the script with the commands
> 
>     $ sh runner.sh A=B

Same here.  eval "$*"

>      $ sh runner.sh A=BCD

OK.

>      $ sh runner.sh A="B    CD"

And here it breaks.  The quotes in your command will *not* be preserved
in the script arguments, so when you expand "$*" you get

A=B    CD

Then eval-ing that gives an error.  No amount of manipulation of "$1"
vs. "$*" vs. "$@" is going to help you here.  Your approach is simply
not going to work.

> I am curios what is going on and if there is any possibility of
> running a command (any command, including an assignment) by using
> a very simple invocation (someting like eval "$@").

I have to wonder what your actual use case is.  There are things that
can be done with a script, and things that can be done with an alias,
and some of these things SHOULD NOT be done.

Let's take the classic horror story: su.

su dbuser -c 'mydb start'

That works fine, because there's nothing fancy in the command.  The entire
command has to be passed as one string, because it's fed to a shell, and
then parsed by that shell.  But since the command consists only of two
alphanumeric words, with no internal quoting, it's acceptable in this
form.

Of course, this gets really ugly, really quickly.

su dbuser -c 'mydb start password="apple sauce"'

You can add a single layer of quoting inside the command, by nesting "..."
inside '...'.  Anything beyond this, and you run up against syntax terrors.

For your "runner.sh" you could pass the command with an extra layer of
quoting around it:

sh runner.sh 'A="B    CD"'

And that will work with eval "$1" or eval "$*".  But it's terrible, just
like su is terrible.  It has precisely the same limitations, because it's
using precisely the same mechanism.

So... what do you *really* want?  Is "sh runner.sh" a stand-in for some
kind of ssh-like or Docker-like tool, where you want to type a command
locally but run it in a different environment (different account, different
computer, different container, etc.)?

Or are you looking for something that will log your commands exactly as
they were typed (in a place that isn't shell history) before running them?

Or something entirely different?



reply via email to

[Prev in Thread] Current Thread [Next in Thread]