3

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.

xdevs23
  • 3,824
  • 3
  • 20
  • 33
  • Not sure what you are asking, but if you want to create a function on one line, you can use `function f() { echo hi; }`. Note the final `;`. – Mark Setchell Aug 03 '16 at 11:46
  • Well, I want to get the **content** of a function in one line in one string. – xdevs23 Aug 03 '16 at 12:11
  • Why do you need the content of the functions instead of just running the functions themselves? Because you need to embed the calls in the argument to `repo` (whatever that is)? Does `type addXos` do (most of) what you need? Or actually `declare -f addXos`? – Etan Reisner Aug 03 '16 at 12:23
  • Because, for whatever reason, the `repo` tool does not accept that. I could be writing the stuff into the quotes but I am lazy :P – xdevs23 Aug 03 '16 at 12:37
  • From your question, it's unclear what `repo all -c` does with the next positional argument. Does it run shell substitution on it (like `bash -c`), or does it interpret it as a command? The last 2 code snippets suggest different things. – Matei David Aug 03 '16 at 14:35
  • While this doesn't generally apply to every scenario, I found that doing it like this: `repo forall -c "bash -ic '...'"` works, where `...` are the commands to be run. The important part here is the `i` flag which passes the environment from the parent process to the child. – xdevs23 Apr 14 '20 at 19:17

5 Answers5

4

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
  • That's almost what I need. Can you also tell me how I can remove `foo is a function` and `foo() { }`, only keeping the lines themselves in one single line like this: `echo foo; echo foo2; echo foo3;...`, assuming that those commands are in that particular function? – xdevs23 Aug 03 '16 at 12:36
2

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.

Matei David
  • 2,322
  • 3
  • 23
  • 36
1

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"
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • Even though your answer might be correct, I am facing following error message: `Got an error, terminating the pool: OSError: [Errno 2] No such file or directory`. Seems to be related to the `repo` tool. Anyways, this does not help me in this particular situation. Do you know how I can echo the **content** of a function, means all single commands? – xdevs23 Aug 03 '16 at 12:24
1

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
agc
  • 7,973
  • 2
  • 29
  • 50
  • 1
    Caveat: some folks regard most any language's form of `eval` as **ee**_ville_, [not entirely without reason](http://stackoverflow.com/questions/2571401/why-exactly-is-eval-evil?s=2|2.9495). – agc Aug 03 '16 at 16:40
  • This works. Just copy&pasted that `in_func` function, then just ran the following: `repo forall -c "$(in_func addAosp) $(in_func addXos) $(in_func addCaf)`. But it is a bit buggy with the `repo` tool. Well, this answer is the correct one. – xdevs23 Aug 04 '16 at 11:26
  • 1
    Removing spacing and adding `;`-s won't work with higher bash constructs such as `while`, `for`, `if`. E.g.: `boo () { while false; do echo bar; done; echo ok; }; eval $(in_func boo)` gives a syntax error. (The problem is that `in_func` adds a `;` right after `do`.) My answer somewhere below is not limited in this way, but alas this was accepted. – Matei David Aug 04 '16 at 14:11
  • @MateiDavid, thanks for the bug report. I should have tried `in_func in_func`, since it too contains a `while`. Have now added a `do` check to the answer `sed` code. – agc Aug 04 '16 at 18:22
  • Revised `do` correction -- just stick a `;` on the end *only*. Should fix the problems *MateiDavid* pointed out. (Couldn't figure out how to do it without invoking `sed` twice.) – agc Aug 04 '16 at 19:03
0

This should work:

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

Jawad
  • 4,457
  • 1
  • 26
  • 29