4

Is there a way that stdin can 'hop' over a process? For example, in the following command,

cat file | ssh host 'mkdir -p /some/directory && cat > /some/directory/file'

This will send the stdin from the first cat to mkdir and the second cat will recieve no stdin. I want the stdout from the first cat to hop over mkdir and only be sent to the second cat. I am aware that you can run something like:

cat file | ssh host 'cat > /tmp/file2 ; mkdir -p /some/directory && mv /tmp/file2 /some/directory/'

That only works when copying a file or

cat file | ssh host 'tee >(mkdir -p /some/directory) >/some/directory/file'

But that only works because the mkdir command does not use stdin. Is there a command that will execute a command that replicates this functionality? Something like:

cat file | ssh host 'stdinhop mkdir -p /some/directory | cat > /some/directory/file'

where stdinhop would not send its stdin to mkdir, but it redirect it to stdout so the second cat can read it?

2 Answers 2

4

You can redirect the first command's stdin from /dev/null:

anthony@Watt:~$ echo -e 'hello\nworld' | ssh localhost 'cat < /dev/null && cat -n'
     1  hello
     2  world

The lines are numbered, so the 2nd cat got them.

If not using ssh in there, you'd use a subshell: echo -e 'hello\nworld' | ( cat < /dev/null && cat -n )

2
  • @don_crissti true, I figured OP wanted to run a command that insists on reading STDIN.
    – derobert
    Commented Mar 7, 2015 at 20:29
  • @don_crissti - exactly. But, essentially, the { grouping; } is already happening here. There's a single fork point - ssh. It kind of serves the same purpose here, but is a little more like out | { { group; } ; { group; }; { group; }; } and so it still needs protecting from the shell's i/o init for every group. In the same way you can absorb the input into a group like that, you can also skip it by being explicit about redirections - or both could work. You could skip over an interval group, maybe, by redirecting /dev/null into the group's stdin.
    – mikeserv
    Commented Mar 7, 2015 at 22:58
2

What you want is already happening, actually. And certainly mkdir isn't your problem - it doesn't read stdin anyway. That pipe is inherited as stdin by all of the children of ssh - unless, that is, you are getting a pseudo terminal like ssh -t.

Barring that, then the problem is that the shell running those commands is one of ssh's children - and part of its job is to open 0,1,2 for each command group it runs. Your input, then, is just getting lost in the shuffle because it is on a common descriptor - the same descriptor that the shell reopens for every one of its children.

And so most simply, I think, you can just move the input to a descriptor that is other than one of those three defaults. For example, the following works for me:

echo hey there >file
cat file | ssh mikeserv@localhost '
               exec 3<&0
               mkdir -p .
               echo ho there
               cat <&3'

If I run the above it prints:

ho there
hey there

You might also find some utility in the -W option to ssh. man ssh says:

  • -W host :port - Requests that standard input and output on the client be forwarded to host on port over the secure channel. Implies -N, -T, ExitOnForwardFailure and ClearAllForwardings. Works with Protocol version 2 only

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .