7

I have a Linux executable foo that reads input from fd 0 and writes output to fd 0 (not fd 1). This works just fine for interactive use in the terminal.

From the shell command line, how can I execute this program such that reads from fd 0 come from one file, but writes to fd 0 go into a different file?

The best I can come up with is $ goo input.txt output.txt | foo where goo is some helper program, but I don't know of any existing helper program, and I don't even know if the pipe set up by | is bidirectional.

  • 1
    Pipes are unidirectional. The only way to do this would be to replace FD 0 with a socket connected to a program which handles the read/writes. Whoever wrote the program which writes to FD 0 needs to be slapped around a bit. – Patrick Jul 30 '14 at 13:18
  • Thanks. foo <> /dev/tcp/localhost/1234 in one window and nc -l 1234 < input.txt > output.txt in another seems to work! – Jay Jul 30 '14 at 13:29
  • 1
    Another solution would be to use a LD_PRELOAD library to override write() so that write() calls to FD 0 are changed to FD 1. – Patrick Jul 30 '14 at 13:32
  • @Patrick, the LD_PRELOAD idea is clever. Assuming it can be used (foo is not statically linked, not setuid or setgid, etc), and assuming that it can't just be fixed at the source code level and rebuilt, that seems like it might be a pretty good long-term solution. – godlygeek Jul 30 '14 at 14:09
  • ... in that it could be set up in a wrapper script, with foo moved out of $PATH and the wrapper replacing it, which would let there be a foo in the $PATH that works the way it should have been written in the first place. – godlygeek Jul 30 '14 at 14:42
6

With socat (version 2 or above):

socat 'system:cat input.txt & cat > output.txt,commtype=socketpair' \
      'system:foo,nofork'

Or even better:

socat 'CREATE:output.txt%OPEN:input.txt' 'system:foo,commtype=socketpair'

We're using a socketpair which is bidirectional (pipes are not bidirectional on Linux). Another option is to use a pseudo-terminal (bidirectional as well) by using commtype=pty above. That may be a better way if foo expects to talk to a terminal.

Or you implement the socketpair approach in perl like:

perl -MSocket -e '
  socketpair(A, B, AF_UNIX, SOCK_STREAM, PF_UNSPEC);
  if (fork) {
    close A;
    if (fork) {
      open STDOUT, ">&B";
      exec("cat", "input.txt")
    }
    open STDIN, "<&B";
    open STDOUT, ">output.txt";
    exec("cat")
  }
  close B;
  open STDIN, "<&A";
  exec "foo"'

(here as a quick proof-of-concept, you may want to add error checking and make it more efficient by not using that many processes and avoid executing cat).

  • with socat 1.7.2.3 the first one gives me E parseopts(): unknown option "commtype" and the second one gives me E CREATE: wrong number of parameters (3 instead of 1). – Jay Jul 31 '14 at 9:16
  • @Jay, yes, there have been a great number of new features in version 2. Those commands rely on some of them. – Stéphane Chazelas Jul 31 '14 at 9:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.