23

Kind of a tricky one to name this...

Basically I have a program which when run prints on STDOUT a set of shell variables:

$ ./settings
SETTING_ONE="this is setting one"
SETTING_TWO="This is the second setting"
ANOTHER_SETTING="This is another setting".

I want to run this from within a shell script as if the STDOUT were being evaluated with source.

I'd like to do something like ...

source `./settings`

... but of course that doesn't work.

I know I could do:

./settings >/tmp/file
source /tmp/file

but I really don't want to do that.

Any clues?

Majenko
  • 32,428

5 Answers5

27

On systems where /dev/fd is available, bash supports process substitution:

source <(./settings)

Here, <( ) will expand to an automatically assigned path under /dev/fd/... from which the output of ./settings can be read.

u1686_grawity
  • 452,512
23

You can use eval:

eval "$(./settings)"

eval "`./settings`"
Keith
  • 8,133
  • Sorry, should have mentioned, it's /bin/sh not bash. $() doesn't work. I have updated the question. – Majenko Apr 18 '11 at 20:19
  • @Matt: Well, sh is bash on most systems. Unless of course you meant recent Ubuntu versions, where it has been replaced with dash. – Hello71 Apr 18 '11 at 20:24
  • @Matt: In that case, backticks should work. But you should add the exact version of sh too - it could be a symlink to dash, ash, busybox... I have not seen a copy of "the real 'sh'" live. – u1686_grawity Apr 18 '11 at 20:29
  • 1
    @Matt: So you've got an ... interesting system there. Especially since almost all "sh" variations support $( ) -- starting with Almquist's shell in 4.3BSD -- and it's POSIX too. (Note: not arguing, just curious.) – u1686_grawity Apr 18 '11 at 20:31
  • $() exists, it just doesn't work like that in this circumstance. FreeBSD 8.2's /bin/sh – Majenko Apr 18 '11 at 20:32
  • Well, it didn't when I tried it just now - now it does. Strange - must be having a 'moment'... – Majenko Apr 18 '11 at 20:34
  • 1
    $( ) doesn't work in heirloom shell (which predates POSIX). – Rufflewind Aug 14 '15 at 23:31
7
declare `./settings`

Or of course...

export `./settings`

Test it of course...

export `echo -e "asdf=test\nqwerty=dvorak"` ; echo $asdf $qwerty

Handling whitespace:

eval export `./settings`
u1686_grawity
  • 452,512
Hello71
  • 8,517
  • 5
  • 40
  • 45
6

source /dev/stdin < ./settings

I think /dev/stdin is a Linux only thing though.

LawrenceC
  • 73,957
  • that tries to source the content of settings. Even with './settings' it fails with './settings': Ambiguous (' = backtick) – Majenko Apr 18 '11 at 20:18
  • 2
    /dev/stdin works on BSD and Cygwin, too. – u1686_grawity Apr 18 '11 at 20:20
  • 1
    Using |, however, is not going to work (at least not exactly), because both sides of the pipe are separate subprocesses, so sourced commands would not affect the current shell. – u1686_grawity Apr 18 '11 at 20:22
  • 1
    edited to reflect that. – LawrenceC Apr 18 '11 at 20:46
  • 2
    I like this /dev/stdin trick, but what your answer does is in fact equivalent to a plain source ./settings without executing it. One could use a here-document to overcome this: source /dev/stdin <<EOF \n $(./settings) \n EOF. – tlwhitec May 27 '19 at 09:45
  • 2
    @tlwhitec, or herestring (if available): source /dev/stdn <<<"$(./settings)". But process substitution (if available) is even shorter: source <(./settings). – Sasha Aug 11 '19 at 08:02
  • Note that settings | source /dev/stdin does actually work under ksh. ksh(1) says of pipelines: Each command, except possibly the last, is run as a separate process, which is different to bash. The "except possibly the last" really helps here! – user7761803 Jan 19 '22 at 16:20
1

Wanted to provide another perspective here, as the other answers create files and don't directly pull from stdin. I have some builds that need to send some prepared environment information to multiple scripts. What I'm doing is preparing a bunch of Bash compatible variable assignments in a string:

Var1="Foo"
Var2="Bar"
Var3="Baz"

When I'm preparing to execute the script, I base64 encode the above multiline string and pipe it into my shell script:

echo base64EncodedParameters | build.sh

In build.sh I read from stdin, base64 decode and eval the result.

params=""
while read line; do params="${params}${line}"; done
eval `echo $params | base64 -D`

echo "Hello ${Var1} ${Var2}"