150

I have a tmp.txt file containing variables to be exported, for example:

a=123
b="hello world"
c="one more variable"

How can I export all these variables using the export command, so that they can later be used by child processes?

2
290
set -a
. ./tmp.txt
set +a

set -a causes variables¹ defined from now on to be automatically exported. It's available in any Bourne-like shell. . is the standard and Bourne name for the source command so I prefer it for portability (source comes from csh and is now available in most modern Bourne-like shells including bash though (sometimes with a slightly different behaviour)).

In POSIX shells, you can also use set -o allexport as a more descriptive alternative way to write it (set +o allexport to unset).

You can make it a function with:

export_from() {
  # local is not a standard command but is pretty common. It's needed here
  # for this code to be re-entrant (for the case where sourced files to
  # call export_from). We still use _export_from_ prefix to namespace
  # those variables to reduce the risk of those variables being some of
  # those exported by the sourced file.
  local _export_from_ret _export_from_restore _export_from_file

  _export_from_ret=0

  # record current state of the allexport option. Some shells (ksh93/zsh)
  # have support for local scope for options, but there's no standard
  # equivalent.
  case $- in
    (*a*) _export_from_restore=;;
    (*)   _export_from_restore='set +a';;
  esac

  for _export_from_file do
    # using the command prefix removes the "special" attribute of the "."
    # command so that it doesn't exit the shell when failing.
    command . "$_export_from_file" || _export_from_ret="$?"
  done
  eval "$_export_from_restore"
  return "$_export_from_ret"
}

¹ In bash, beware that it also causes all functions declared while allexport is on to be exported to the environment (as BASH_FUNC_myfunction%% environment variables that are then imported by all bash shells run in that environment, even when running as sh).

4
  • if variable's value has blank, the second run will failed
    – jk2K
    Nov 25 '19 at 14:01
  • 1
    What does set +a do?
    – learner
    Jun 24 '20 at 5:11
  • With set, -<char> turns it on, and +<char> turns it off. Jun 11 at 15:17
  • This was awesome, by the way. After struggling for an hour to figure out why manually exporting a bunch of variables wasn't working as expected, I simply added this flag and boom! Worked. Thanks! Jun 11 at 15:18
114
source tmp.txt
export a b c
./child ...

Judging by your other question, you don't want to hardcode the variable names:

source tmp.txt
export $(cut -d= -f1 tmp.txt)

test it:

$ source tmp.txt
$ echo "$a $b $c"
123 hello world one more variable
$ perl -E 'say "@ENV{qw(a b c)}"'

$ export $(cut -d= -f1 tmp.txt)
$ perl -E 'say "@ENV{qw(a b c)}"'
123 hello world one more variable
3
  • 4
    This won't work if the environment file contains comments, for example. (eg. files that can be reused by systemd's EnvironmentFile)
    – Chris Lamb
    Nov 12 '17 at 22:36
  • 8
    @ChrisLamb you can use grep to skip comments: export $(grep --regexp ^[A-Z] tmp.txt | cut -d= -f1)
    – gvee
    Jul 2 '18 at 16:05
  • 2
    The one-line version that ignore comments into your .env file source .env && export $(sed '/^#/d' .env | cut -d= -f1) Jun 15 at 13:21
9

A dangerous one-liner that doesn't require source:

export $(xargs <file)
  • It can't handle comments, frequently used in environment files
  • It can't handle values with whitespace, like in the question example
  • It may unintentionally expand glob patterns into files if they match by any chance

It's a bit dangerous because it passes the lines through bash expansion, but it has been useful to me when I know I have safe environment files.

5
  • 2
    You should try that on the example given in the question. You will notice that you get the wrong values due to the splitting that the shell does on whitespaces.
    – Kusalananda
    Nov 30 '19 at 22:05
  • Oops, one more caveat, though a very strong one.
    – villasv
    Nov 30 '19 at 22:07
  • 2
    Also, if you're unlucky, a line may contain filename globbing characters that would pull in filenames.
    – Kusalananda
    Nov 30 '19 at 22:11
  • I guess I'm just going to outright flag the answer as dangerous. Thanks for the warnings @Kusalananda
    – villasv
    Nov 30 '19 at 22:20
  • 1
    "xargs nothing? why does this work?" I wondered. xargs man page answered: If **utility** is omitted, echo(1) is used
    – conny
    Jan 13 at 9:07
3

Just do:

while read LINE; do export "$LINE"; done < ./tmp.txt
4
  • Did you check whether that works?
    – RalfFriedl
    Jul 1 '19 at 20:33
  • @RalfFriedl this should work, why not? It's not very elegant and sourcing with set -a would be far simpler, but this does actually work.
    – terdon
    Jul 2 '19 at 8:15
  • 3
    This is brittle. It does not allow comments in the input file, does not handle quoted variable values properly, and fails on multi-line variables. Granted I don't have many multi-line variables, but I do use comments regularly and often need to use quotes for variable values.
    – Louis
    Jul 24 '19 at 15:53
  • 1
    It worked for me.
    – mchawre
    Aug 7 '19 at 5:26
1

This solution will export all key=values to environment variables that are in .env file, that are not empty lines or commented (#).

File: .env

ENV=local
DEBUG=True

Command:

$ export $(cat .env | egrep -v "(^#.*|^$)" | xargs)
1
  • 1
    values of multiple words must be quoted. This only removes lines which have # as first character on line, comments starting after a value are not removed
    – X Tian
    Mar 23 at 11:40
0

little workaround based on one of answers:

  1. create function and place it in ~/.bashrc
function myenvs() {

    if [ -z "$1" ]; then
        echo "Usage: myenvs [import file path]";
    else
        if [ -f "$1" ]; then
            source "$1" 2>/dev/null; export $(cat "$1" | grep "=" | grep -v "^#" | awk /./ | cut -d= -f1 | xargs)
        else
            echo "Bad file path: $1"
        fi
    fi
}
  1. run $ myenvs /path/to/env/file to import envs

※ if env/file has bad lines, here are errors may be appeared when source called. I just hide it, so error handling is up on you

0

If you need a (basic) solution that apply an export to each line (no eval nor source) use this script:

#!/bin/bash -

exec 5<abc.env

while read -r -u 5 line ; do
    line=${line%% \#*}                        # remove line comments.
    line="${line%"${line##*[![:space:]]}"}"   # remove trailing space characters.
    [[ $line == "" ]] && continue             # avoid empty lines.
    export "$line";
done 

exec 5>&-

echo "A=<$A> " "B=<$B> " "C=<$C> " "D=<$D>"

Assuming an abc.env file that contains:

$ cat ./abc.env
A=1
B=2
C="3 4 5"
D="7 8"     # comment added
            # empty line

the script above will print:

A=<1>  B=<2>  C=<"3 4 5">  D=<"7 8">

The exec is required to have a loop without a subshell. A while ... do ... done <file will place the while loop inside a subshell ( no export command will work).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.