xdevs23
xdevs23

Reputation: 3994

How to get the content of a function in a string using bash?

I have already searched about this particular problem, but couldn't find anything helpful.

Let's assume I have following functions defined in my ~/.bashrc (Note: this is pseudo-code!):

ANDROID_PLATFORM_ROOT="/home/simao/xos/src/"

function getPlatformPath() {
  echo "$ANDROID_PLATFORM_ROOT"
}

function addCaf() {
  # Me doing stuff
  echo "blah/$(getPlatformPath)"
}

function addAosp() {
  # Me doing stuff
  echo "aosp/$(getPlatformPath)"
}

function addXos() {
  # Me doing stuff
  echo "xos/$(getPlatformPath)"
}

function addAllAll() {
  cd $(gettop)
  # repo forall -c "addCaf; addAosp; addXos" # Does not work!
  repo forall -c # Here is where I need all those commands
}

My problem:

I need to get the functions addCaf, addAosp and addXos in one single line.

Like you can run following in bash (pseudo code):

dothis; dothat; doanotherthing; trythis && succeedsdothis || nosuccessdothis; blah

I would like to run all commands inside the three functions addCaf, addAosp and addXos in just one line.

Any help is appreciated.

What I already tried:

repo forall -c "bash -c \"source ~/.bashrc; addAllAll\""

But that didn't work as well.

Edit:

To clarify what I mean.

I want something like that as a result:

repo forall -c 'function getPlatformPath() { echo "$ANDROID_PLATFORM_ROOT"; }; ANDROID_PLATFORM_ROOT="/home/simao/xos/src/"; echo "blah/$(getPlatformPath)"; echo "aosp/$(getPlatformPath)"; echo "xos/$(getPlatformPath)"'

But I don't want to write that manually. Instead, I want to get those lines from the functions that already exist.

Upvotes: 3

Views: 2364

Answers (5)

agc
agc

Reputation: 8406

Make a dummy function foo(), which just prints "bar":

foo() { echo bar ; }

Now a bash function to print what's in one (or more) functions. Since the contents of a function are indented with 4 spaces, sed removes any lines without 4 leading spaces, then removes the leading spaces as well, and adds a ';' at the end of each function:

# Usage: in_func <function_name1> [ ...<function_name2> ... ]
in_func() 
    { while [ "$1" ] ; do \
          type $1 | sed  -n '/^    /{s/^    //p}' | sed '$s/.*/&;/' ; shift ; \
      done ; }

Print what's in foo():

in_func foo

Output:

echo bar;

Assign what's in foo() to the string $baz, then print $baz:

baz="`in_func foo`" ; echo $baz

Output:

echo bar;

Run what's in foo():

eval "$baz"

Output:

bar

Assign what's in foo() to $baz three times, and run it:

baz="`in_func foo foo foo`" ; eval "$baz"

Output:

bar
bar
bar

Upvotes: 1

Matei David
Matei David

Reputation: 2362

Assuming that repo forall -c interprets the next positional argument just as bash -c, try:

foo () { 
    echo "foo!"
}
boo () { 
    if true; then
        echo "boo!"
    fi
}
echo works | bash -c "source "<(typeset -f foo boo)"; foo; boo; cat"

Note:

  • The difference from the original version is that this no longer interferes with stdin.

  • The <(...) substitution is unescaped because it must be performed by the original shell, the one where foo and boo are first defined. Its output will be a string of the form /dev/fd/63, which is a file descriptor that is passed open to the second shell, and which contains the forwarded definitions.

Upvotes: 2

Vicente Olivert Riera
Vicente Olivert Riera

Reputation: 1220

You can use type and then parse its output to do whatever you want to do with the code lines.

$ foo() {
> echo foo
> }

$ type foo
foo is a function
foo () 
{ 
    echo foo
}

Perhaps this example makes things more clear:

#!/bin/bash

foo() {
    echo "foo"
}

bar() {
    echo "bar"
}

export IFS=$'\n'

for f in foo bar; do
    for i in $(type $f | head -n-1 | tail -n+4); do
        eval $i
    done
done

exit 0

This is how it looks:

$ ./funcs.sh 
foo
bar

What the script is doing is first loop over all the functions you have (in this case only foo and bar). For each function, it loops over the code of that function (skipping the useless lines from type's output) and it executes them. So at the end it's the same as having this code...

echo "foo"
echo "bar"

...which are exactly the code lines inside the functions, and you are executing them one after the other.

Note that you could also build a string variable containing all the code lines separated by ; if instead of running eval on every line you do something like this:

code_lines=

for f in foo bar; do
        for i in $(type $f | head -n-1 | tail -n+4); do
                if [ -z $code_lines ]; then
                        code_lines="$i"
                else
                        code_lines="${code_lines}; $i"
                fi  
        done
done

eval $code_lines

Upvotes: 4

John Kugelman
John Kugelman

Reputation: 361595

Shell functions aren't visible to child processes unless they're exported. Perhaps that is the missing ingredient.

export -f addCaf addAosp addXos
repo forall -c "addCaf; addAosp; addXos"

Upvotes: 1

Jawad
Jawad

Reputation: 4665

This should work:

repo forall -c "$(addCaf) $(addAosp) $(addXos)"

Upvotes: 0

Related Questions