The most naive way to interact with another program is simply to run it, let it communicate directly with the user, and wait for it to finish. This is done with the
run command.
i1 : run "uname -a"
Darwin gallium 22.5.0 Darwin Kernel Version 22.5.0: Mon Apr 24 20:51:50 PDT 2023; root:xnu-8796.121.2~5/RELEASE_X86_64 x86_64
o1 = 0
|
To run a program and provide it with input, one way is use the operator
<<, with a file name whose first character is an exclamation point; the rest of the file name will be taken as the command to run, as in the following example.
i2 : "!grep a" << " ba \n bc \n ad \n ef \n" << close
ba
ad
o2 = !grep a
o2 : File
|
More often, one wants to write Macaulay2 code to obtain and manipulate the output from the other program. If the program requires no input data, then we can use
get with a file name whose first character is an exclamation point. In the following example, we also peek at the string to see whether it includes a newline character.
i3 : peek get "!uname -a"
o3 = "Darwin gallium 22.5.0 Darwin Kernel Version 22.5.0: Mon Apr 24 20:51:50
"
PDT 2023; root:xnu-8796.121.2~5/RELEASE_X86_64 x86_64
|
Bidirectional communication with a program is also possible. We use
openInOut to create a file that serves as a bidirectional connection to a program. That file is called an input output file. In this example we open a connection to the unix utility
egrep and use it to locate the symbol names in Macaulay2 that begin with
in.
i4 : f = openInOut "!egrep '^in'"
o4 = !egrep '^in'
o4 : File
|
i5 : scan(keys Core.Dictionary, key -> f << key << endl)
|
i6 : f << closeOut
o6 = !egrep '^in'
o6 : File
|
i7 : get f
o7 = interpreterDepth
infoHelp
integrate
inducedMap
installPackage
installMethod
insert
instance
info
interval
intersect
inversePermutation
installedPackages
input
inverseErf
index
indexComponents
installAssignmentMethod
infinity
intersection
inducesWellDefinedMap
inverseRegularizedGamma
inverse
indices
incomparable
installHilbertFunction
indeterminate
in
inverseRegularizedBeta
instances
independentSets
|
With this form of bidirectional communication there is always a danger of blocking, because the buffers associated with the communication channels (pipes) typically hold only 4096 bytes. In this example we succeeded because the entire output from
egrep was smaller than 4096 bytes. In general, one should be careful to arrange things so that the two programs take turns using the communication channel, so that when one is writing data, the other is reading it.
A useful function in this connection is
isReady, which will tell you whether an input file has any input available for reading, or whether it has arrived at the end. We illustrate it in the following example by simulating a computation that takes 5 seconds to complete, printing one dot per second while waiting.
i8 : f = openIn "!sleep 5; echo -n the answer is 4"
o8 = !sleep 5; echo -n the answer is 4
o8 : File
|
i9 : isReady f
o9 = false
|
i10 : while not isReady f do (sleep 1; << "." << flush)
......
|
i11 : read f
o11 = -n the answer is 4
|
i12 : isReady f
o12 = true
|
i13 : atEndOfFile f
o13 = true
|
i14 : close f
o14 = !sleep 5; echo -n the answer is 4
o14 : File
|
We also allow for bidirectional communication through sockets over the internet. See
openInOut and
openListener, or the next section.
Another useful function is
wait, which can be used to wait for input to be available from any of a list of input files.