Search code examples
smalltalkpharosqueak

How to interact with a subprocess through its stdin, stdout, stderr in Smalltalk?


This Python code shows how to call some process in Windows 10 and to send to it string commands, to read its string responses through stdin, stdout pipes of the process:

Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from subprocess import *
>>> p = Popen("c:/python38/python.exe", stdin=PIPE, stdout=PIPE)
>>> p.stdin.write(b"print(1+9)\n")
11
>>> p.communicate()
(b'10\r\n', None)
>>>

As you can see the python.exe process returned 10 as an answer to print(1+9). Now I want to do the same in Pharo (or Squeak): in Windows 10 OS - I suppose something similar, i.e. short, simple, understandable, really working.

I installed OSProcess, ProcessWrapper (they were missing in Pharo, also its strange that I got warning that they are not marked for Pharo 8.0 and were not checked to work in Pharo 8.0, but OK), and I tried ProcessWrapper, PipeableOSProcess (copy-pasted different snippets from the Web), etc - with zero success! The results were:

  • nothing happens, python.exe was not started
  • VM errors console was opened (white console in the bottom of the Pharo, which is controlled with F2 menu)
  • different exceptions
  • etc

Would somebody show me simple working example how to start a process and to to send it commands, read answers, then send again, and so on in some loop - I plan to have such communication in a detached thread and to use it as some service, because Pharo, Smalltalk in general is missing most bindings, so then I will use subprocess communication like in "good" old days...

I know how to call a command and to get its output:

out := LibC resultOfCommand: 'dir ', aDir.

but I am talking about another scenario: a communication with a running process interactively (for example, with SSH or similar like in the example above - python.exe).

PS. Maybe it's possible to do it with LibC #pipe:mode even?


Solution

  • Let me start with that the PipeableOsProcess is probably broken on Windows. I have tried it and it just opened a command line and nothing else (it does not freeze my Pharo 8). The whole OSProcess does not work correctly in my eyes.

    So I took a shot at LibC which is supposed to not work with Windows.

    I’m a module defining access to standard LibC. I’m available under Linux and OSX, but not under Windows for obvious reasons :)

    Next is to say that Python's Windows support is probably much better than Pharo's.

    The solution, which is more like a workaround using files, is to use LibC and #runCommand: (I tried to come up with a similar example as you had shown above):

    | count command result outputFile errorFile  |
    
    count := 9+1.  "The counting"
    command := 'echo ', count asString. "command run at the command line"
    
    outputFile := 'output'. "a file into which the output is redirected"
    errorFile := 'error'. "a file where the error output is redirected "
    
    result := LibC runCommand: command, "run the command "
        ' >', outputFile, "redirect the output to output file"
        ' 2>', errorFile.
    
    "reading back the value from output file"
    outputFile asFileReference contents lines.
    "reading back the value from the error file - which is empty in this case" 
    errorFile asFileReference contents lines.