2

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?

5
  • 2
    Does this answer helps you ? Commented May 25, 2018 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. Commented May 25, 2018 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. Commented May 25, 2018 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. Commented May 25, 2018 at 14:42
  • 1
    Since you're sourceing the file, you could just put its contents into the string sent to the remote host for execution. ssh user@host "$(</tmp/settings.conf); $(declare -f); display_os_name" Commented May 25, 2018 at 17:05

2 Answers 2

2

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"
Sign up to request clarification or add additional context in comments.

2 Comments

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!
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.
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
    $(typeset -f display_os_name)
    display_os_name

EOT
#################################################################################

1 Comment

typset appears to be a typo -- should be typeset or declare.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.