1

I have a script like this that declares many functions which use another functions, etc.:

#!/bin/bash

function a {
    ...
}

function b {
    ...
    a
    ...
}

...

And another script that uses few functions from that script:

#!/bin/bash

do_something

source "/path/to/functional/script"

ssh user@host "$(typeset -f f_from_that_script); f_from_that_script"
...

The problem that it says environment: line N: another_f_from_that_script: command not found, that means that the first function uses the second one internally:

function f_from_that_script {
    ...
    another_f_from_that_script
    ...
}

Is there a way to define all those function to be able to run them via ssh?

3 Answers 3

1

No, the parser in bash is not able to do a syntax analysis this deep (and it might generally not be possible, since the names of called functions might be computed at run time). To the best of my knowledge, there is no third-party Parser that could do that, but papers describing why it is even theoretically impossible to do in all cases. I have written an answer about that.

Instead of forwarding a single function, I'd recommend you run the full script containing all the functions. You could either do that by scp'ing a script to a temporary location and then sourceing it via ssh, or by just piping the whole script into stdin through ssh.

If you're having to execute complex functionality over ssh regularly, maybe a more mighty, formalized remote administration tool like ansible is the way to go.

0

You can always pass all the known function definitions to the remote shell:

#! /bin/bash -

do_something

source "/path/to/functional/script" || exit

{
  typeset -f # output all function definitions
  echo f_from_that_script
} | ssh user@host bash

Note that sshd on host would run user's login shell to interpret the command that you pass, which would not necessarily be bash (who would use bash as their login shell in this day and age?).

Also beware that bash's syntax is dependant on:

  • the locale (particularly the LC_CTYPE category: charset and character classification)
  • the set of options (like extglob) being enabled.
  • the version of bash

So beware of potential problems if the locale and version of bash is different between the local and remote host or you change some of options from the default.

-2

you can manually load the functions by:

. /path/to/functional/script

Then it will load that script into the current environment, then the second script can access those functions.

To automate it, simple add at the bottom of your ~/.bashrc

. /path/to/functional/script

that should fix your issue.

I must be misunderstanding the issue or the error is in ssh command.

Here is my test:

File: function_source.sh

#!/bin/bash

function testcall {

echo "now here I am"

}

file do_something.sh

#!/bin/bash

source ./function_source.sh

echo -e "This is called from within the script:\r\n"
testcall
echo -e "End Script - Any further output is from the function call:\r\n\r\n"

Script file do_something.sh loads the source file of function_source.sh and then calls the function testcall, that is in function_source.sh.

ssh %server% "./do_something.sh; testcall"

Output:

-ssh %server% "./do_something.sh; testcall"

This is called from within the script:

now here I am

End Script - Any further output is from the function call:

bash: line 1: testcall: command not found

Now if I reference function_source.sh is .bashrc with the below line added to the end of .bashrc:

. ./function_source.sh

Now I run the same command:

ssh %server% "./do_something.sh; testcall"

and the output is:

-ssh %server% "./do_something.sh; testcall"

This is called from within the script:

now here I am

End Script - Any further output is from the function call:

now here I am

The only way referencing the file in .bashrc does not work is if you have the below in your .bashrc:

case $- in
    *i*) ;;
      *) return;;
esac

If you do have that code in your .bashrc, then when you run a command using ssh, it will not load your .bashrc enviroment, and therefore will not load your source file.

Note: This is why you do not just use standard bashrc and script file, you either write them from scratch yourself, or you read every line and know what ever line does.

To cover off the other setups, loading the source file first, then calling the function - Note: I have removed the reference to the source in bashrc:

command:

ssh %server% ". ~/function_source.sh; testcall"

Output:

-ssh %server% ". ~/function_source.sh; testcall"

now here I am

and finally, loading the source file in bashrc, and just calling the function using ssh.

command:

ssh %server% "testcall"

Output:

-ssh %server% "testcall"

now here I am

Let me know if you need any more examples.

Note: I used a windows Command Prompt, with a modified Prompt of -, to run the ssh commands, the %server% variable was used for privacy reasons, and ssh keys were used to authenticate.

To ensure I cover off on everything, I moved to a ssh in one of my linux vm's, so I can ssh to localhost as terdon suggested.

I also modified function source:

-cat function_source.sh
#!/bin/bash

function testcall {

echo -e "now here I am\r\n\r\n"
testanother
}

function testanother {
        echo -e "this is another function\r\n\r\n"
}
 

If I run the command:

ssh localhost "$(typeset -f testcall); testcall"

output:

-ssh localhost "$(typeset -f testcall); testcall"

now here I am

However, if I run the command:

ssh localhost "$(cat function_source.sh); testcall"

Output:

-ssh localhost "$(cat function_source.sh); testcall"

now here I am

this is another function

You would only need to do this, if you did not have write access to the server you were ssh to, and needed to load the functions into the environment each time.

8
  • 1
    The source command in the OP's script is an alias for . in bash, so the OP is already doing exactly what you say.
    – terdon
    Commented Sep 26, 2023 at 9:18
  • I didnt say to use the source command inside the script. When you load source inside the script, you are adding the function script to the shell instance that the script is running in, if you run another command, that opens a new instance of bash, the functions will not be loaded in that environment, because that instance is not a child of the instance the script is running in. Commented Sep 27, 2023 at 15:54
  • What I suggested, was to load the source file, either in bashrc or load it into the environment from the cmd line before running the script. Therefore when a new cmd is run, the parent of that instance will have the functions loaded in its environment, and therefore will be able to use those functions. Commented Sep 27, 2023 at 15:55
  • I suggest to the two down voters, that you create this example in a test environment, and then review your down vote accordingly Commented Sep 27, 2023 at 15:56
  • Yes, which is precisely what the OP is doing and it fails because they need to run on a remote environment and have nested functions. We have done this in a test environment, may I suggest that you try it too? Your answer is exactly what the OP is already doing: they are sourcing the file with the functions in the script before launching the ssh command, so the parent process of the ssh command has the functions defined.
    – terdon
    Commented Sep 27, 2023 at 15:58

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.