63

Is there any clever way to run a local Bash function on a remote host over ssh?

For example:

#!/bin/bash
#Definition of the function
f () {  ls -l; }

#I want to use the function locally
f

#Execution of the function on the remote machine.
ssh user@host f

#Reuse of the same function on another machine.
ssh user@host2 f

Yeah, I know it doesn't work, but is there a way to achieve this?

tripleee
  • 175,061
  • 34
  • 275
  • 318
Mr.Eddart
  • 10,050
  • 13
  • 49
  • 77

3 Answers3

131

You can use the typeset command to make your functions available on a remote machine via ssh. There are several options depending on how you want to run your remote script.

#!/bin/bash
# Define your function
myfn () {  ls -l; }

To use the function on the remote hosts:

typeset -f myfn | ssh user@host "$(cat); myfn"
typeset -f myfn | ssh user@host2 "$(cat); myfn"

Better yet, why bother with pipe:

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

Or you can use a HEREDOC:

ssh user@host << EOF
    $(typeset -f myfn)
    myfn
EOF

If you want to send all the functions defined within the script, not just myfn, just use typeset -f like so:

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

Explanation

typeset -f myfn will display the definition of myfn.

cat will receive the definition of the function as a text and $() will execute it in the current shell which will become a defined function in the remote shell. Finally the function can be executed.

The last code will put the definition of the functions inline before ssh execution.

zekel
  • 9,227
  • 10
  • 65
  • 96
alvits
  • 6,550
  • 1
  • 28
  • 28
  • 4
    Better to use `typeset -f f` and only send the definition of the one function across – Henk Langeveld Mar 01 '14 at 01:46
  • 10
    @HenkLangeveld - It depends whether there are required functions called by f(). In my assumption, the function f() might need other functions. Otherwise your suggestion would be best. – alvits Mar 01 '14 at 05:58
  • How can i pass some parameters to this function? – Daniel Sep 30 '15 at 00:40
  • 3
    The same way you would any commands. If the function is `f()` then you can pass parameters like `f param1 param2 ...`. Inside `f()` you will reference the parameters as `$1, $2, ... $n`. – alvits Sep 30 '15 at 01:17
  • 2
    Excellent! I used `declare -f` instead of `typeset -f` for Bash. THanks. – Felipe Alvarez Apr 21 '16 at 05:14
  • is it a way to load another sh file, where the function is defined?when i use it says "COMMAND NOT FOUND" like function is not defined! – Mojtaba Pourmirzaei Mar 14 '18 at 11:46
  • @alvits Good. But what if I have a `main.sh` and a `util.sh`, and I reference the `util.sh` within the `main.sh`. How should I execute `main.sh` via ssh without copying `utils.sh` to remote machine ? – Lewis Chan Dec 24 '18 at 09:05
  • 2
    I get a `syntax error near unexpected token ;` when using either `declare -f` or `typset -f` – guygrinberger Feb 03 '20 at 09:35
  • `declare`/`typeset` didn't work in a HEREDOC for me. It only works in `""`. However, newlines and tabs are ignored as whitespace in strings, so I could still indent all my code inside the string nicely. – alx - recommends codidact Oct 04 '20 at 20:44
  • If `myfn` uses variables, add `env` (probably with a grep to select which ones to send): `ssh user@host "$(typeset -f myfn); $(env); myfn"` – tokland May 17 '22 at 07:40
7

I personally don't know the correct answer to your question, but I have a lot of installation scripts that just copy themselves over using ssh.

Have the command copy the file over, load the file functions, run the file functions, and then delete the file.

ssh user@host "scp user@otherhost:/myFile ; . myFile ; f ; rm Myfile"
user2836202
  • 641
  • 3
  • 12
5

Another way:

#!/bin/bash
# Definition of the function
foo () {  ls -l; }

# Use the function locally
foo

# Execution of the function on the remote machine.
ssh user@host "$(declare -f foo);foo"

declare -f foo print the definition of function