27

Is it possible to do this:

ssh user@socket command /path/to/file/on/local/machine

That is to say, I want to execute a remote command using a local file in one step, without first using scp to copy the file.

iconoclast
  • 9,198
  • 13
  • 57
  • 97
jsj
  • 1,410
  • It's possible if the remote computer has a network mount of a parent directory of the file of the local machine or if you redirect the file into the ssh command – h3rrmiller Oct 26 '12 at 17:24

3 Answers3

27

You missed just one symbol =)

ssh user@socket command < /path/to/file/on/local/machine
rush
  • 27,403
  • Of course! I wonder why I didn't consider input redirection over ssh?! – jsj Oct 26 '12 at 13:08
  • 1
    What if remote command can only take a file argument and not read from stdin? – iruvar Oct 26 '12 at 14:48
  • @ChandraRavoori in that case you need to copy it with scp before. – rush Oct 26 '12 at 15:53
  • 4
    @ChandraRavoori You can try giving it the file argument /dev/stdin or -. May or may not work (/dev/stdin is a file, but seeking it will fail) – derobert Oct 26 '12 at 16:03
  • 1
    @derobert and rush, played around with it a bit and discovered a hacky way to do it using process substitution under bash. This is still subject to the seeking limitation and can get unwieldy with all the additional quoting that will be needed. Example follows. cat test.file | ssh user@machine 'bash -c "wc -l <(cat -)"' – iruvar Oct 26 '12 at 19:04
4

One way that works regardless of the command is to make the file available on the remote machine via a remote filesystem. Since you have an SSH connection:

  1. Establish a reverse SSH tunnel. See also SSH easily copy file to local system
  2. Mount a directory tree of your machine containing the file to share on the remote machine with SSHFS. (Example)
  • Anyway this way requires additional movements before command execution. – rush Oct 27 '12 at 06:09
  • @Gilles: I think the original title was misleading, and you understood his question based on the misleading title. As far as I can tell, thought, he didn't actually want to act on** the local file, but just pass it in as input. (But your answer is amazing, if I understand it correctly! Maybe I should ask the question you answered, just to give you a place to put the answer where you'll get proper credit for it.) I've edited the questions title to make it consistent with the body and with the chosen answer. – iconoclast Aug 01 '14 at 16:10
  • @iconoclast I don't remember my state of mind when I wrote this, but I go by this answer. It requires some setup but has the advantage of generalizing beyond the immediate requirement to other cases where it isn't enough to copy the input. I'd also have mentioned redirection if someone else hadn't done it. The more general question has been asked many times in slightly different forms. – Gilles 'SO- stop being evil' Aug 01 '14 at 17:02
  • Okay, I must have misunderstood your answer. I didn't try it, but inferred that since you were establishing a reverse tunnel (from remote to local host?) you were allowing the local file to be modified by the remote machine. Is that not the case? – iconoclast Aug 01 '14 at 17:21
  • @iconoclast Yes, this allows the remote machine to modify the local file (in addition to allowing it to read it, which is what the question requires). – Gilles 'SO- stop being evil' Aug 01 '14 at 17:22
  • Okay, I thought you were taking this approach instead of < since you thought writing was required (as was implied by the original title) but you were simply offering another way of doing it (which just happens to allow writing as a side effect). – iconoclast Aug 01 '14 at 17:25
1
# What if remote command can only take a file argument and not read from stdin? (1_CR)
ssh user@socket command < /path/to/file/on/local/machine
...
cat test.file | ssh user@machine 'bash -c "wc -l <(cat -)"'  # 1_CR

As an alternative to bash process substitution <(cat -) or < <(xargs -0 -n 1000 cat) (see below) you can just use xargs and cat to pipe the contents of the specified files to wc -l (which is more portable).

# Assuming that test.file contains file paths each delimited by an ASCII NUL character \0
# and that we are to count all those lines in all those files (provided by test.file).

#find . -type f -print0 > test.file
# test with repeated line count of ~/.bash_history file
for n in {1..1000}; do printf '%s\000' "${HOME}/.bash_history"; done > test.file

# xargs & cat
ssh localhost 'export LC_ALL=C; xargs -0 -n 1000 cat | wc -l' <test.file

# Bash process substitution
cat test.file | ssh localhost 'bash -c "export LC_ALL=C; wc -l < <(xargs -0 -n 1000 cat)"'
frank
  • 11