5

Let's start with simple tests that work for me to check that source /dev/stdin can be used at all.

# echo -ne 'echo a\necho b\n' | source /dev/stdin
a
b

Now I would like to source an actual function.

# echo -ne 'f() { echo a; }\n' | source /dev/stdin
# f
-bash: f: command not found

Now let's try with a temporary file.

# echo -ne 'f() { echo a; }\n' > tempf
# source tempf
# f
a

So the temporary file works. But it's very incovenient in my case and I don't see any valid reason why the pipe shouldn't work just as well.

# bash --version
GNU bash, version 4.2.53(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

For completeness, the actual use case is to carfully select which parts of a file will be included, in order to work around over a limitation in Gentoo portage.

post_src_unpack() {
    if type epatch_user > /dev/null 2>&1; then
        epatch_user || die
    else
        awk \
            '/^# @FUNCTION: / { p = 0 } /^# @FUNCTION: epatch(_user)?$/ { p = 1; } p { print  }' \
            /usr/portage/eclass/eutils.eclass | source /dev/stdin || die
        epatch_user || die
        unset epatch
        unset epatch_user
    fi
}

The purpose of the code is to extract just two required functions epatch and epatch_user from a source file with lots of functions, make them available in the current shell, run one of them (which in turn uses the other), and remove them. The final goal is to workaround the limitation of Gentoo that only ebuilds inheriting eutils have access to epatch_user.

  • 1
    I guess you could put your awk command inside a $() process substitution, and then eval that... if you can guarantee that the string you're passing to eval is safe. – PM 2Ring Dec 15 '14 at 13:13
3

You can use process substitution

source /dev/stdin < <(echo -ne 'f() { echo a; }\n')

or

source <(echo -ne 'f() { echo a; }\n')

This works in bash 4.1.5, for some reason it doesn't work in 3.2.48.

  • I like the second one better, the /dev/stdin is clearly redundant. The process substitution is bash-specific, right? It's local Gentoo scripting so I think it's safe to require bash. The requirement for a recent version is trivial in that case. – Pavel Šimerda Dec 15 '14 at 21:33
  • It's hard to choose the best answer as some answers contain an explanation which was very helpful to me, some offer a portable way which may be useful for others, but this one definitely contains what I would like to use to avoid running source in a subshell. – Pavel Šimerda Dec 15 '14 at 21:34
  • 1
    Process substitution isn't POSIX, but it's in bash, ksh, and zsh. – Barmar Dec 15 '14 at 21:59
4

Each part of pipelines run in separated processes, or own subshell. So when your pipelines finished, your current shell does not know anything about function f.

With bash (ksh, pdksh, zsh, mksh or shell that support Here-String), your can use:

$ source /dev/stdin <<<'f() { echo a; }'
$ f
a

POSIXly, you should use Here-Document and dot:

$ . /dev/stdin <<'EOF'
> f() { echo a; }
> EOF

$ f
a
  • The explanation works for me, indeed I wasn't aware of the subshells. The solution works for the example but not for the original task that is at the end of the question. – Pavel Šimerda Dec 15 '14 at 20:52
2

The commands in a pipe are separate processes, hence the function definition that is sourced from /dev/stdin is lost as soon as the pipe completes. That is why the pipe show different results to the usage of the temporary file.

In your use case the eval as suggested by PM 2Ring would be the way to go.

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.