Shell – Run local script with local input file on remote host

io-redirectionshell-scriptsshstdin

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)?

Best Answer

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)

Related Question