18

Homebrew requires us to add eval $(/opt/homebrew/bin/brew shellenv) to ~/.zprofile. What does this actually evaluate to and what does this accomplish?

I am a bit new to shell scripting. I know $var is used to refer to a variable named var but that's about it. What is the meaning of putting a path inside of $()? Also is shellenv an argument here and what does it signify?

Kusalananda
  • 333,661
Osbridge
  • 183

2 Answers2

12

$() is command substitution. It executes the command inside the parentheses and returns the output of that command.

It's often used to get the output of a program into a variable. e.g.

$ month=$(date +%m)
$ echo $month
02

or to use the output of one program as args for another, e.g.

$ printf '%s\n' $(date +%B)
February

(ok, yeah, printf is a contrived example because just running date +%B produces the same output...but it does demonstrate how it works).

The command inside the parentheses may be as simple or as complex as you need, from a single program to a long pipeline of commands. e.g. I often use commands like the following to remove old kernel packages from my Debian system:

apt-get purge $(dlocate -k | grep '6\.0\.0-[245]')

BTW, you may also see scripts using backticks (`) for this. This is an old, obsolete form of command substitution, supported for legacy reasons but shouldn't be used in new scripts.

When used with eval, it causes your shell to execute the output. In this case, running /opt/homebrew/bin/brew shellenv returns a bunch of text like var=value and eval runs them in your current shell, thus setting those variables to the required values.

From help eval in bash:

eval: eval [arg ...]

Execute arguments as a shell command.

Combine ARGs into a single string, use the result as input to the shell, and execute the resulting commands.

Exit Status: Returns exit status of command or success if command is null.

Try running /opt/homebrew/bin/brew shellenv by itself to see what output it produces.

cas
  • 78,579
  • Thank you for the explanation. That was really it. I first tried running /opt/homebrew/bin/brew shellenv with eval $(/opt/homebrew/bin/brew shellenv) still in ~/.zprofile but it gave no output so got a bit confused. Then I removed it and it gave a bunch of export commands which clearly show how it appends brew to $PATH.

    Thanks for help!

    – Osbridge Feb 17 '23 at 02:11
  • 2
    @Osbridge brew shellenv not giving any output at all under some circumstances is explained by brew help shellenv (mentioned in a recent answer here: Command output evaluation not working in Bash script). – Kusalananda Feb 17 '23 at 07:21
  • /opt/homebrew/bin/brew shellenv has no output. I have no idea why in MacOS - they make everything so complicated? I just want to apend the PATH and for that i am searching around for last 3+ hours. – Ashu Sep 06 '23 at 02:14
4

It exports environment variables needed for brew to work. For example, if I run it without eval then this is what it outputs in fish shell:

$ /opt/homebrew/bin/brew shellenv

set -gx HOMEBREW_PREFIX "/opt/homebrew"; set -gx HOMEBREW_CELLAR "/opt/homebrew/Cellar"; set -gx HOMEBREW_REPOSITORY "/opt/homebrew"; set -q PATH; or set PATH ''; set -gx PATH "/opt/homebrew/bin" "/opt/homebrew/sbin" $PATH; set -q MANPATH; or set MANPATH ''; set -gx MANPATH "/opt/homebrew/share/man" $MANPATH; set -q INFOPATH; or set INFOPATH ''; set -gx INFOPATH "/opt/homebrew/share/info" $INFOPATH;

Then eval actually executes those set commands.

Important to note that this outputs nothing if it has already been loaded into the session.

Elijah Lynn
  • 1,045