Passing External Shell Script Variable via Ssh

Passing external shell script variable via ssh

Your problem: Your entire command is put into single quotes – obviously so that bash expressions are expanded on the server and not locally.

But this also applies to your $1.

Simple solution: “Interupt” the quotation by wrapping your local variable into single quotes.

ssh root@192.168.0.1 'echo "#date added $(date +%m/%d/%Y)" >> /var/named/chroot/etc/named.conf; echo "zone \"'$1'\" { type master; file \"/etc/zone/dummy-block\"; };" >> /var/named/chroot/etc/named.conf'

NB: \"$1\"\"'$1'\".

NOTE: This solution is a simple fix for the one-liner as posted in the question above. If there's the slightest chance that this script is executed by other people, or it could process external output of any kind, please have a look at Charles Duffy's solution.

Passing a variable to a remote host in a bash script with ssh and EOF

Use printf %q to escape content in an eval-safe form; after doing so, you can pass them on the command line of the remote shell, and retrieve them via $1, $2, etc. within the remote script:

# put contents of $VAR into $var_str in a format that a shell can interpret
printf -v var_str %q "$VAR"

# v- pass the value on the shell command line
# | v- keep escaping the heredoc securely
# | |
ssh -T -p 1234 root@"$host" "bash -s $var_str" <<'EOF'

# retrieve it off the shell command line
var=$1

# ...and use it as you like thereafter.
echo "Remotely using $var"
EOF

Passing variables in remote ssh command

If you use

ssh pvt@192.168.1.133 "~/tools/run_pvt.pl $BUILD_NUMBER"

instead of

ssh pvt@192.168.1.133 '~/tools/run_pvt.pl $BUILD_NUMBER'

your shell will interpolate the $BUILD_NUMBER before sending the command string to the remote host.

Passing variables to SSH

As an approach that doesn't require you to do any manual escaping of your code (which frequently becomes a maintenance nightmare, since it means that code needs to be changed whenever you modify where it's expected to run) -- consider defining a function, and using declare -f to ask the shell to generate code that will output that function for you.

The same can be done with variables, using declare -p. Thus, passing both a function with the remote code, and the variables that remote code needs to operate that way:

#!/usr/bin/env bash

# This is run on the remote server _as root_ (behind sudo su)
remotePostEscalationFunc() {
cd /home/jenkins/report || return
if psql -d db -U user -c "$(sed -e "s/state_name/${state}/" county.sql)"; then
echo "Success processing $state" >&2
else
rc=$?
echo "Failure processing $state" >&2
return "$rc"
fi
}

# This is run on the remote server as the jenkins user (before sudo).
remoteFunc() {
sudo su -c "$(declare -p state); $(declare -f remotePostEscalationFunc); remotePostEscalationFunc"
}

# Everything below here is run locally.

arr_states=( NY CO )

for state in "${arr_states[@]}"; do
ssh jenkins@server 'bash -s' <<EOF
$(declare -f remoteFunc remotePostEscalationFunc); $(declare -p state); remoteFunc
EOF
done

How to pass local variables to ssh scope in bash script?

The problem has nothing to do with ssh but is only related to here documents in bash or any other Posix shell.

Man page for bash says in the here document paragraph:

The format of here-documents is:

<<[-]word

here-document

delimiter

No parameter expansion, command substitution, arithmetic expansion, or pathname expansion is performed on word. If any characters in word are quoted, the delimiter is the result of quote removal on word, and the lines in the here-document are not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion. In the latter case, the character sequence \<newline> is ignored, and \ must be used to quote the characters \, $, and ` .

As you quote EOF, you explicitely ask the shell not to replace the $1 and $2 variables.

The most robust way is to not quote EOF and consistently quote all others special characters in the here document.

For example, if your here document contained something like:

ssh -T user@servername << 'EOF'
for f in /var/log/messages*
do echo "Filename: " $f
done
EOF

you could rewrite it with no quotes around EOF but with one inside $:

ssh -T user@servername << EOF
for f in /var/log/messages*
do echo "Filename: " \$f
done
EOF

That way all unquoted varibles would be interpolated.


Alternatively, if the server allows it, you can try to pass the 2 parameters as environment variables.

Say you want to use the names PARAM1 and PARAM2. The sshd_config file on the server should contain the line AcceptEnv PARAM1 PARAM2 because by default and for security reasons no environment variable is accepted.

You can then use:

export PARAM1=$1
export PARAM2=$2
ssh -T -o SendEnv=PARAM1 -o SenEnv=PARAM2 user@servername << 'EOF'
...
Using variables $PARAM1 and $PARAM2
...
EOF

Using external variable in awk being passed to ssh in bash

I have the following test.sh:

#! /bin/bash
host=$1
mytime=$2
pastusers=`ssh -n $host "last -n 10" | awk -v TIME=$mytime '{if($7>TIME) print $1}'`
echo $pastusers

When I run bash test.sh <some_valid_host> 20:00 it prints a list of users as expected, all in the same line. Those being in the same line is also expected.

If you have a file test.txt like:

abc
def ghi

Running

words=`cat test.txt`; echo $words

will print abc def ghi all in same line. This is the expected behavior.

Passing variable through ssh doesnt work

Try using double quotes, that should evaluate the variable locally:

ssh $USER@some_host "qsub $working_dir/some_file.txt"

Command line Parameters in bash shell script in nested ssh

Use printf %q to generate an eval-safe string version of your argument list:

# generate a string which evals to list of command-line parameters
printf -v cmd_str '%q ' "$@"

# pass that list of parameters on the remote shell's command line
ssh "$host" "bash -s $cmd_str" <<'EOF'
echo "This is running on the remote host."
echo "Got arguments:"
printf '- %q\n' "$@"
EOF

For what you're really doing, the best practice is probably to use a ProxyCommand -- see the relevant documentation -- and to have your private key exposed via agent forwarding, rather than having it sitting on your bounce host on-disk. That said, it's straightforward enough to adopt the answer given above to fit the code in the question:

#!/bin/bash
printf -v args '%q ' "$@"
echo "Arguments on original host are:"
printf '- %q\n' "$@"
ssh -t "StrictHostKeyChecking=no" -i "$1" user@ip "bash -s $args" <<'EOF1'
printf -v args '%q ' "$@"
echo "Arguments on ip1 are:"
printf '- %q\n' "$@"
ssh -t -i "$1" user2@ip2 "bash -s $args" <<'EOF2'
echo "Arguments on ip2 are:"
printf '- %q\n' "$@"
EOF2
EOF1


Related Topics



Leave a reply



Submit