2

This would probably never be the BEST approach to something, but I'm wondering if it's even possible.

Something like:

awk '/function_i_want_to_call/,/^$/{print}' script_containing_function | xargs source

function_i_want_to_call arg1 arg2 arg3

Except actually working.

  • The difference between source or . (dot) and just executing a script is whether it is done in your original shell process or in a new (subshell) process. For permanently setting variables or other shell properties (like cd) you need the first, which the answers address. If you only need to execute the commands you can pipe to a new shell like awk '/select/,/lines/' script | sh. – dave_thompson_085 Nov 14 '15 at 2:14
  • Right, I'm aware of that. Though it just occurred to me that if the variables needed to be set are in the parent shell, they could be passed through with "export" and then just calling the other script normally. But it was the "only part of the other script" that was the focus of my question. – Wildcard Nov 14 '15 at 3:59
3

First you need to rigorously determine what command will produce the specific part you want to source. For a trivial example, given the file

var1=value1
var2=value2

you could set only var1 using head -n1 filename. This could be a pipeline of arbitrary complexity, if you wanted.

Then run:

source <( pipeline_of_arbitrary_complexity some_filename )

Works only in bash. To do it in POSIX, I think you'd need to make a temp file.

  • I didn't realize <( command ) was bash-specific. But that's definitely the simplest answer, thanks. – Wildcard Jan 13 '16 at 4:12
3

In bash/ksh and similar, you can use <() to avoid the subshell that | creates:

insourcable.sh:

echo hallo welt
echo hello world
echo ahoj světe

Suppose you only want to in-source line number two:

. <( sed -n '2 p' < insourcable.sh )

(prints "hello world")

  • @mikeserv was alluding to what the OP was trying to do in the question, namely source in from the right-hand side of a pipeline. Obviously, it wouldn't work with xargs but it could work with something like echo echo foo | . /dev/stdin but that wouldn't be very useful because what's on the right hand side of a this pipeline is a subshell that cannot affect the parent shell. – PSkocik Nov 13 '15 at 20:22
1
dotfn(){ . /dev/fd/0
} <<IN
        $(printf '\n%s(){ "$@" '"'\n" "$2"
          sed   -e"/$2.*{/,\$!d;s/^/    /"\
                -e"/^. *} *$/q;s/'/'"'\\&&/g'  <"$1"
          printf "\n';}")
IN

obviously it's not fool proof, but it scans a file named in its first argument for the first series of input that begins with its second arg followed at some point on the same line with a { and pulls in all following lines until it encounters one which only matches blank space and a single }.

while sed does so it escapes all hard-quotes in input, and printf wraps the input in two hard-quotes and also prepends it with the string "$@". it sticks all of this in a function named for your second argument. here's an example:

printf '
some_fn(){
        echo some stuff
        cat </dev/null
}

another_fn(){
        for f in file*; do printf '\''%s\n'\'' "$f"; done
}
' >fn_file

i put those two functions in fn_file, and then...

dotfn fn_file another_fn

...nothing happened. but...

another_fn printf %s\\n

another_fn(){
    for f in file*; do printf '%s\n' "$f"; done
}

i enclosed the function within one named for itself, and i can inspect it with printf or whatever, or i can redefine it:

another_fn eval; another_fn

file
file1.tsv
file1.txt
file2.tsv
file2.txt
filea.xyz
fileb.xyz

I replaced the . /dev/fd/0 in dotfn() with a simple cat and went after some_fn():

dotfn fn_file some_fn

some_fn(){ "$@" '
    some_fn(){
        echo some stuff
        cat </dev/null
    }

';}

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.