Search code examples
crystal-lang

Is there a way to pipe IO?


I'm trying to pipe 2 IO object, i come from nodejs and we can do something like that:

const child_process = require('child_process')

const shell = child_process.spawn('/bin/sh')

shell.stdout.pipe(process.stdout)
shell.stdin.write('pwd\n')
shell.stdin.write('ls\n')
/* write all command i want */

and im looking for do the same thing in crystal

i know for the current example we can write

shell = Process.new("/bin/sh", input: Process::Redirect::Pipe, output: STDOUT, error: STDOUT)

shell.input << "ls\n"
shell.input << "pwd\n"
# all commands i want

but for some reason passing TCPSocket to Process.new input/output/error dont work very well (see here too if you have time Process and TCPSocket not close properly in crystal)

so im looking for an alternative way who will look like:

shell = Process.new("/bin/sh", input: Process::Redirect::Pipe, output: Process::Redirect::Pipe, Process::Redirect::Pipe)

shell.output.pipe(STDOUT) # not the crystal pipe but like the nodejs pipe

shell.input << "ls\n"
shell.input << "pwd\n"
# all commands i want

Solution

  • You can use IO.copy inside a coroutine:

    shell = Process.new("/bin/sh", input: :pipe, output: :pipe, error: :pipe)
    
    spawn { IO.copy shell.output, STDOUT }
    spawn { IO.copy shell.error, STDERR }
    
    shell.input << "ls /\n"
    shell.input << "pwd\n"
    
    shell.wait
    

    https://carc.in/#/r/75z4