I have written an bash script foo.sh
#!/usr/bin/env bash
echo \"starting the script\";
I want to execute it in my remote server.
I tried
Use the -s
option, which forces bash
(or any POSIX-compatible shell) to read its command from standard input, rather than from a file named by the first positional argument. All arguments are treated as parameters to the script instead.
ssh user@remote-addr 'bash -s arg' < test.sh
bash
or ksh
as /bin/sh
If your remote /bin/sh
is provided by bash or ksh, you can safely do the following with an untrusted argument list, such that even malicious names (like $(rm -rf $HOME).txt
) can be passed as arguments safely:
runRemote() {
local args script
script=$1; shift
# generate eval-safe quoted version of current argument list
printf -v args '%q ' "$@"
# pass that through on the command line to bash -s
# note that $args is parsed remotely by /bin/sh, not by bash!
ssh user@remote-addr "bash -s -- $args" < "$script"
}
...thereafter:
runRemote test.sh testparam
/bin/sh
Note that the following still needs to be run in bash
, but will work correctly when the system being ssh
'd into has a /bin/sh
that is POSIX-baseline, so long as the remote machine has bash installed.
To be safe against sufficiently malicious argument data (attempting to take advantage of the non-POSIX compliant quoting used by printf %q
in bash when nonprintable characters are present in the string being escaped) even with a /bin/sh
that is baseline-POSIX (such as dash
or ash
), it gets a bit more interesting:
runRemote() {
local script=$1; shift
local args
printf -v args '%q ' "$@"
ssh user@remote-addr "bash -s" <<EOF
# pass quoted arguments through for parsing by remote bash
set -- $args
# substitute literal script text into heredoc
$(< "$script")
EOF
}
Similarly invoked as:
runRemote test.sh testparam