26

bash and fish scripts are not compatible, but I would like to have a file that defines some some environment variables to be initialized by both bash and fish.

My proposed solution is defining a ~/.env file that would contain the list of environment variables like so:

PATH="$HOME/bin:$PATH"
FOO="bar"

I could then just source it in bash and make a script that converts it to fish format and sources that in fish.

I was thinking that there may be a better solution than this, so I'm asking for better way of sharing environment variables between bash fish.

Note: I'm using OS X.


Here is an example .env file that I would like both fish and bash to handle using ridiculous-fish's syntax (assume ~/bin and ~/bin2 are empty directories):

setenv _PATH "$PATH"
setenv PATH "$HOME/bin"
setenv PATH "$PATH:$HOME/bin2"
setenv PATH "$PATH:$_PATH"

7 Answers 7

23

bash has special syntax for setting environment variables, while fish uses a builtin. I would suggest writing your .env file like so:

setenv VAR1 val1
setenv VAR2 val2

and then defining setenv appropriately in the respective shells. In bash (e.g. .bashrc):

function setenv() { export "$1=$2"; }
. ~/.env

In fish (e.g. config.fish):

function setenv; set -gx $argv; end
source ~/.env

Note that PATH will require some special handling, since it's an array in fish but a colon delimited string in bash. If you prefer to write setenv PATH "$HOME/bin:$PATH" in .env, you could write fish's setenv like so:

function setenv
    if [ $argv[1] = PATH ]
        # Replace colons and spaces with newlines
        set -gx PATH (echo $argv[2] | tr ': ' \n)
    else
        set -gx $argv
    end
 end

This will mishandle elements in PATH that contain spaces, colons, or newlines.

The awkwardness in PATH is due to mixing up colon-delimited strings with true arrays. The preferred way to append to PATH in fish is simply set PATH $PATH ~/bin.

6
  • 1
    Will the PATH fix for fish work with paths with: spaces? newlines? other things to look out for?
    – Tyilo
    Commented Dec 28, 2014 at 23:28
  • [ is a builtin in both bash and fish, so PATH should not need to be set correctly to use [. The setenv sample should work with all characters in PATH except for colons and newlines. Commented Dec 30, 2014 at 0:35
  • Replacing tr with /usr/bin/tr I and using the .env file provided in my question, I get a lot of errors when starting fish. I don't think the PATH handling is correct.
    – Tyilo
    Commented Dec 30, 2014 at 0:39
  • Oh, right, fish will space-separate it inside quotes. I'll update the comment. Commented Dec 31, 2014 at 7:50
  • Yay it works now. I don't think any other shell (except fish?) allows paths with colons in PATH anyway.
    – Tyilo
    Commented Dec 31, 2014 at 16:38
10

There is (now?) an easier way, per @Zanchey's answer here

Fish Shell: How to set multiple environment variables from a file using export

The digest though is:

Fish:

echo -e "foo=3\nfoobar=4" > .env; export (cat .env); env | grep foo

Bash:

echo -e "foo=3\nfoobar=4" > .env; export $(cat .env | xargs); env | grep foo

with the difference being $ and the use of xargs

3
  • Much more convinient and without any special syntax. Thank you
    – Panayotis
    Commented Jun 27, 2018 at 20:18
  • 1
    Doesn't work for arrays such as $PATH
    – JanKanis
    Commented Jul 8, 2019 at 8:49
  • 1
    Doesn't work if you have comments in the .env file. Commented May 23, 2021 at 10:19
8

Most Unix systems use PAM. The pam_env module reads a file very much like your .env.

On Linux, pam_env reads a system file /etc/environment and a user file ~/.pam_environment. On OS X (and other *BSD, which likewise use OpenPAM), it appears that pam_env only reads the system file, so you can't set variables per user this way, only for all users.

1
  • 1
    ~/.pam_environment is no longer read as of PAM 1.4.
    – Devon
    Commented Jan 14, 2023 at 8:53
5

Just create a function in $HOME/.config/fish/functions/env.fish with content:

for f in /etc/profile.d/*.sh
    sh $f
end

shared all vars created in bash in folder /etc/profile.d

for example: /etc/profile.d/golang.sh

#!/bin/sh
export GOPATH=$HOME/gopath
export GOROOT=$HOME/go
export GOBIN=$HOME/gobin

export PATH=$PATH:$GOROOT/bin:$GOBIN

just login on fish or bash, do echo $GOPATH $GOROOT $GOBIN $PATH and see the magic :)

4

Create a file where you will store all your environment variables, named for example ~/.config/env_variables. In this file, add export lines, like this:

# This file is meant to compatible with multiple shells, including:
# bash, zsh and fish. For this reason, use this syntax:
#    export VARNAME=value

export EDITOR=vim
export LESS="-M"
export GOPATH="$HOME/.local/share/gopath/"
export PATH="$PATH:/custom/bin/"

In your ~/.config/fish/config.fish file, include:

source ~/.config/env_variables

In your ~/.bashrc file, include:

source ~/.config/env_variables
1

Expanding on @ridiculous_fish's answer, with the following function for bash it also works for arrays that contain spaces, such as $PATH if you have spaces in your paths. However setenv is already defined as a function in the fish standard library nowadays so I changed the name here to shenv

function shenv { 
        var="$1"
        shift
        export "$var=$(printf "%s\n" "$@" | paste -s -d: -)"
}
. ~/.env

Now in your .env file you can specify array components as separate arguments to the setenv command:

shenv PATH /my/path /my/path\ with\ spaces "/my/quoted path" $PATH

For fish @ridiculous_fish's simple implementation will do:

function shenv; set -gx $argv; end
source ~/.env
0

If you don't mind fork another process, then:

env $(cat ~/.env) fish

I use this way to share the same .env file with multiple shells in multiple projects. The syntax is easy, and it is quite flexible since you can exit the shell to regain your original environment.

More example:

env $(cat .env) bash

env $(cat .env) zsh

env $(cat .env) sh

One drawback is that it assumes .env only in VAR=value syntax (no comment, newline, or quotes).

I prefer to keep .env as simple as possible by naming variables more sensemaking instead of writing lots of comments and newlines. However, if there are some cases that comments and newlines cannot be avoided, it can still be easily solved by piping output to grep, sed or awk.

Sorry, my solution may not solve the original question because of the syntax of .env, but I did found it useful for sharing the same .env with different shells. Hope it can help anyone of you :)

7
  • I guess you mean env $(cat ~/.env) fish? Commented May 23, 2020 at 6:52
  • @G-ManSays'ReinstateMonica', the command substitution syntax in fish is with (cmd); $(cmd) is the POSIX syntax. Commented May 23, 2020 at 7:06
  • That assumes ~/.env online contains lines in VAR=value syntax (no comment, no VAR="value", no empty lines...) Commented May 23, 2020 at 7:07
  • @StéphaneChazelas: OK, but, since the answer is giving a command to start a fish, I assumed that it was meant to be run from bash. Perhaps the answer is what Jian meant, but, if he meant “start a fish process, and then use this command to start another fish process”, I believe he should clarify. Commented May 23, 2020 at 7:14
  • @StéphaneChazelas: And your second comment would be clearer if you said “values cannot contain space character(s), whether quoted/escaped or not.”   And I don’t see a problem with empty lines. Commented May 23, 2020 at 7:19

You must log in to answer this question.

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