22

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,128
  • 4
  • 61
  • 81

5 Answers5

26

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
  • 426,297
  • 64
  • 894
  • 966
22

You can use eval:

eval "$(./settings)"

eval "`./settings`"
Rufflewind
  • 127
  • 7
Keith
  • 8,013
  • 1
  • 31
  • 34
  • 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](http://www.freebsd.org/cgi/man.cgi?query=sh&manpath=4.3BSD+NET%2F2) -- 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
  • 426,297
  • 64
  • 894
  • 966
Hello71
  • 8,397
  • 5
  • 40
  • 44
4

source /dev/stdin < ./settings

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

LawrenceC
  • 73,030
  • 15
  • 129
  • 214
  • 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 < – tlwhitec May 27 '19 at 09:45
  • 1
    @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}"
Jay Proulx
  • 11
  • 1