1

I have a shell script file like this:

#!/bin/bash

CONF_FILE="/tmp/settings.conf" #settings.conf contains OS_NAME="Caine Linux"
source $CONF_FILE

display_os_name() { echo "My OS is:" $OS_NAME }

#using the function locally works fine
display_os_name
#displays: My OS is: Caine Linux

#using the function on the remote host doesn't work
ssh user@host "$(declare -f); display_os_name"
#displays: My OS is:

If I remove the -f and I use just ssh user@host "$(declare); display_os_name" it works but displays these errors and warnings:

bash: line 10: BASHOPTS: readonly variable
bash: line 18: BASH_VERSINFO: readonly variable
bash: line 26: EUID: readonly variable
bash: line 55: PPID: readonly variable
bash: line 70: SHELLOPTS: readonly variable
bash: line 76: UID: readonly variable

If I use ssh user@host "$(declare); display_os_name >/dev/null" to suppress the warnings only the output of the function is suppressed (My OS is: Caine Linux), not the warnings.

Is there a way to run local functions together with sourced local files on a remote SSH host?

Don't Panic
  • 13,965
  • 5
  • 32
  • 51
radonys
  • 597
  • 1
  • 9
  • 20
  • 2
    Does this [answer](https://stackoverflow.com/questions/22107610/shell-script-run-function-from-script-over-ssh#22107893) helps you ? – krampstudio May 25 '18 at 12:12
  • @krampstudio, no it doesn't. If I use `typeset -f` only the function is available on the remote host, not the imported settings file. – radonys May 25 '18 at 12:20
  • 1
    Ultimately, everything you want to use on the remote host has to be *sent* to the remote host. `ssh` is just a connection tool, not a distributed computing framework. – chepner May 25 '18 at 13:31
  • `2>/dev/null` instead of `>/dev/null` to suppress the error messages rather than the standard output. Of course, it'd be better to fix the source of those error messages. – Aaron May 25 '18 at 14:42
  • 1
    Since you're `source`ing the file, you could just put its contents into the string sent to the remote host for execution. `ssh user@host "$( – Charles Duffy May 25 '18 at 17:05

2 Answers2

1

An easy approach (if your local side is Linux) is to use set -a to enable automatic export before your source command; copy /proc/self/environ on stdin; and parse it into a set of variables on the remote side.

Because BASHOPTS, EUID, etc. aren't environment variables, this avoids trying to modify them. (If you were complying with POSIX recommendations and using lowercase names for your own variables, you could even go as far as to ignore all-caps variables entirely).

set -a # enable export of all variables defined, **before** the source operation
source /tmp/settings.conf

import_env() {
  while IFS= read -r -d '' item; do
    printf -v "${item%%=*}" "%s" "${item#*=}" && export "$item"
  done
}

cat /proc/self/environ | ssh user@host "$(declare -f); import_env; display_os_name"

Even easier is to just copy the file you want to source over the wire.

ssh user@host "$(declare -f); $(</tmp/settings.conf); display_os_name"
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Thanks Charles, I tried the file copying for now and it works. The script is small for now so I can change my variables to lower-case, but how will that be helpful? How can I "ignore all-caps variables entirely"? Thank you! – radonys May 29 '18 at 08:43
  • 1
    Inside your `while` loop (before the `printf`), `[[ $item =~ ^[[:upper:]]*= ]] && continue` is one way to skip any variable with an all-uppercase name, and thus ensure that you're only transmitting variables inside the namespace POSIX reserves for application use. The advantage here is that anything like `LD_PRELOAD`, `LD_LIBRARY_PATH`, `LC_LOCALE`, etc that isn't part of your script but is local system-level configuration gets ignored; likewise, keeps you safe even if someone *does* flag a shell variable (`BASHOPTS`, etc) for export. – Charles Duffy May 29 '18 at 17:01
0

This method works using GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu)

#!/bin/bash
#################################################################################

source $CONF_FILE

#settings.conf contains OS_NAME="Caine Linux"
CONF_FILE="/tmp/settings.conf"
special_file='!abc123'
OS_NAME='my_server'
    
display_os_name() 
{
    echo "My OS is:" $OS_NAME
}

ssh -tt -q user@host << EOT
    
    CONF_FILE=$CONF_FILE
    special_file=$\\special_file
    OS_NAME=$OS_NAME
    $(typset -f display_os_name)
    display_os_name

EOT
#################################################################################
Arkham Angel
  • 309
  • 1
  • 5
  • 18