Search code examples
linuxshellsmalltalkpharopharo-5

Is it possible to write shell command within Pharo smalltalk?


Like in other programming language, is there a way of running linux shell command in Pharo smalltalk or a simple script ? I would like to have my Pharo image running a script that should be able to automate a tasks and return it to some value. I looked at almost all the documentation around and I couldn't find anything related. Maybe It does not allow such functionality.


Solution

  • Pharo does allow the OS interaction. The best way, in my eyes, is to use OSProcess (as MartinW already suggested).

    Those that think it is a duplicate are missing this part:

    ... running a script that should be able to automate a tasks and return it to some value...

    There is nothing about return value in the invoking shell commands from squeak or pharo

    To get a return value you would do it the following way:

    command := OSProcess waitForCommand: 'ls -la'.
    command exitStatus.
    

    If you print out the above code you will get most probably a 0 as success.

    If you do an obvious error:

    command := OSProcess waitForCommand: 'ls -la /dir-does-not-exists'.
    command exitStatus.
    

    You will get ~= 0 value in my case 512.

    Edit adding more details to cover more ground

    I agree with eMBee that a statement

    return it to some value

    is rather vague. I'm adding information about I/Os.

    As you may know there are three basic IO: stdin, stdout, and stderr. These you need to interact with shell. I'll add these examples first then I'll get back to your description.

    Each of them is represented by instance of AttachableFileStream in Pharo. For the above command you will get initialStdIn (stdin), initialStdOut (stdout), initialStdError (stderr).

    To write into the terminal from Pharo:

    1. stdout and stderr (you stream string into terminal)

      | process |
      
      process := OSProcess thisOSProcess.
      process stdOut nextPutAll: 'stdout: All your base belong to us'; nextPut: Character lf.
      process stdErr nextPutAll: 'stderr: All your base belong to us'; nextPut: Character lf.
      

    Check your shell you should see the output there.

    1. stdin - to get what you typed

      | userInput handle fetchUserInput |
      
      userInput := OSProcess thisOSProcess stdIn.
      handle := userInput ioHandle.
      "You need this in order to use terminal -> add stdion"
      OSProcess accessor setNonBlocking: handle.
      fetchUserInput := OS2Process thisOSProcess stdIn next.
      "Set blocking back to the handle"
      OSProcess accessor setBlocking: handle.
      "Gets you one input character"
      fetchUserInput inspect.
      

    If you want to grab an output from the command into Pharo a resonable way is to use PipeableOSProcess which, as apparent from his name, can be used in conjunction with pipes.

    Simple example:

    | commandOutput |
    
    commandOutput := (PipeableOSProcess command: 'ls -la') output.
    commandOutput inspect.
    

    More complex example:

    | commandOutput |
    
    commandOutput := ((PipeableOSProcess command: 'ps -ef') | 'grep pharo') outputAndError.
    commandOutput inspect.
    

    I like the use of outputAndError because of typos. If you have an incorrect command you will get the error message:

    | commandOutput |
    
    commandOutput := ((PipeableOSProcess command: 'ps -ef') | 'grep pharo' | 'cot') outputAndError.
    commandOutput  inspect.
    

    In this case '/bin/sh: cot: command not found'

    That is about it.

    Update 29-3-2021 The OSProcess works up to Pharo 7. It was not upgraded to work with the changes at Pharo 8 or newer.