1

Just so I understand the difference between executing the command line in Bash Terminal macOS Mojave.

Let's take for example the installation of a package named: stackexchange.pkg

What are the differences between the two command line below? Is the result the same? Pros and Cons?

/usr/sbin/installer -pkg path_to_stackexchange.pkg -target /

sh -c /usr/sbin/installer -pkg 'path_to_stackexchange.pkg' -target '/'
5
  • I do not think the second one (sh -c /usr/sbin/installer -pkg 'path_to_stackexchange.pkg' -target '/') will work. Mar 12, 2020 at 19:35
  • @DavidAnderson Thanks. Any specific reasons?
    – Fabio
    Mar 12, 2020 at 19:36
  • I just ran the command and I get usage output for /usr/bin/installer. Maybe you meant sh -c '/usr/sbin/installer -pkg path_to_stackexchange.pkg -target /'. Or maybe sh -c '$0 $@' /usr/sbin/installer -pkg 'path_to_stackexchange.pkg' -target '/' Mar 12, 2020 at 19:57
  • Assuming that you're trying to run a shell script in both cases, one of the big differences would be that the sh script.sh let's you run a script.sh that doesn't have execute permissions
    – ernie
    Mar 12, 2020 at 20:01
  • @ernie: ....doesn't have execute permissions in a new shell. As opposed to . script.sh or source script.sh which executes in the same shell. Mar 12, 2020 at 20:11

1 Answer 1

4

When you use sh with its -c option, the argument after -c is taken as the command string to run. Thus, when you run

sh -c /usr/sbin/installer -pkg 'path_to_stackexchange.pkg' -target '/'

sh will run the command "/usr/sbin/installer". It will not pass on the rest of its arguments, because they're not part of the command string. If you want to include arguments, you'd need to do something like this:

sh -c "/usr/sbin/installer -pkg 'path_to_stackexchange.pkg' -target '/'"

The double-quotes make that a single long argument to sh, so it'll execute the whole thing and pass -pkg etc as arguments to the installer command.

(BTW, in the shell, quotes don't nest. Nesting quoted strings inside quoted strings -- like the single-quoted arguments inside that double-quoted argument -- isn't really a thing. In this case it does work, because the inside a double-quoted string, single-quotes are just regular characters. In more complicated situations, things can get weird and confusing.)

So what's happening to those other arguments after the command? Well, the command string (the argument after -c) functions a little like a miniature shell script, and those are passed to that mini-script as arguments. But they're not passed to the commands in that mini-script, unless the mini-script explicitly passes them on:

sh -c '/usr/sbin/installer "$@"' installer-thingie -pkg 'path_to_stackexchange.pkg' -target '/'

Here, inside the mini-script, the "$@" expands to the arguments to the mini-script (i.e. -pkg, path_to_stackexchange.pkg, etc.), thus passing them on to the installer command.

But what's that installer-thingie argument? That's the name the mini-script is run under, so it'll show up as $0 inside the mini-script. Basically, it functions as a placeholder, and you could put pretty much anything there. "sh" is a common choice.

As for the pros and cons of using sh -c to run commands: unless there's some reason you really need to run another shell, just run the command directly. Running it with sh -c is more complicated and confusing, and (as you've experienced) easy to get wrong.

As @DavidAnderson pointed out, using sh -c is also less efficient, because it creates an additional shell process. If you look at the process tree for running installer directly (and assuming your interactive shell is zsh), it looks like this:

zsh───installer

But with sh -c, it's like this:

zsh───sh───installer

...it ran an sh process under your interactive shell, and then that ran installer as a subprocess.

On the other hand, if you prefer confusing complexity and inefficiency, there's no reason to stop at one unnecessary shell; why not add another sh -c to run the sh -c command?

sh -c "sh -c \"/usr/sbin/installer -pkg 'path_to_stackexchange.pkg' -target '/'\""

or even

sh -c "sh -c \"sh -c \\\"/usr/sbin/installer -pkg 'path_to_stackexchange.pkg' -target '/'\\\"\""

(did I mention that nesting quotes can get complicated?)

1
  • 2
    I would add that after correcting for the OP's syntax errors, the first command runs in the same shell and the second runs in a new shell. Mar 13, 2020 at 0:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .