Run local script with local input file on remote host
Solution 1
While ssh
provides for two separate output streams (for stdout and stderr), it only feeds one input stream (stdin). So you'd need either to pass the script content and input file via different mechanisms.
For instance, one via a variable, one via stdin:
LC_CODE=$(cat local_script.sh) ssh host 'eval "$LC_CODE"' < input
(assuming both your ssh
client passes the LC_*
variable (SendEnv
in ssh_config
) and the sshd
server accepts them (AcceptEnv
in sshd_config
))
Or simply pass the content of the local_script.sh
as the remote shell code assuming the login shell of the remote user is the right one for the syntax of that script:
ssh host "$(cat local_script.sh)" < input
Or concatenate the code and input fed to ssh
's stdin like with:
{
echo '{'; cat local_script.sh; echo '}; exit'
cat input
} | ssh host 'bash -s some arguments'
Here using bash
because bash
will take care of reading the input one byte at a time so as not to read past that }; exit
line while not all other shells do that.
Or if sed
on the remote host is GNU sed
:
echo '#END-OF-SCRIPT' | cat local_script.sh - input |
ssh host 'set some arguments; eval "$(sed -u "/^#END-OF-SCRIPT/q")"'
(here having the code evaluated by the login shell of the remote user and assuming it's Bourne-like)
Solution 2
Here is a more elegant solution using the BASH 'here-strings' operator ('<<<')
ssh -t host "/bin/bash <(base64 --decode <<<'$(base64 < script)' )"
IT takes the script, converts it to a base64 string, in the SSH command it converts it back and gives it to the bash as a pipe-filename.
For an script to ste up an interactive shell prefix the 'pipe filename' with "--rcfile"
ssh -t host "/bin/bash --rcfile <(base64 --decode <<<'$(base64 < script)' )"
More on... https://antofthy.gitlab.io/info/apps/ssh_remote_commands.txt
Solution 3
This works well and even lets you pass the contents of multiple files to the script:
ssh host "bash -s -- '$(cat local_file_1.txt)' '$(cat local_file_2.txt)'" < local_script.sh
The explanation is that ssh
will execute bash -s
on login with the script arguments being everything after --
. So $1
will be the contents of local_file_1.txt
and $2
will be the contents of local_file_2.txt
. bash -s
reads commands from stdin which comes from < local_script.sh
.
Related videos on Youtube
Nester
Updated on September 18, 2022Comments
-
Nester over 1 year
I have a local script that I want to run on several remote servers. The command syntax I'm using is:
ssh <remote_server> "bash -s" < ./local_script.sh
This works fine, and even lets me pass parameters to local_script.sh. However, I'd really like to pass an input file to it, as in:
local_script.sh < local_file.txt
Combining these two statements gives:
ssh <remote_server> "bash -s" < ./local_script.sh < local_file.txt
When I run this, I get
bash: line 1: FOO: command not found bash: line 2: BAR: command not found bash: line 3: BAZ: command not found ...
where FOO, BAR, BAZ, etc. are the first word on each line of local_file.txt:
FOO FOO_PARAM1 BAR BAR_PARAM1 BAZ BAZ_PARAM2 ...
So, it looks like
"bash -s"
on the remote server is interpreting local_file.txt as a script file, instead of an input file to local_script.sh. Is there any way to fix this (other than creating a wrapper script)? -
Nester over 7 yearsThe second example
ssh host "$(cat local_script.sh)" < input
worked great, and is an elegant solution as well. Thanks! -
Jeremy almost 5 yearsFrom the Bash manual: "The command substitution
$(cat file)
can be replaced by the equivalent but faster$(< file)
."