In the following chapters 21.3.3 to 21.3.6 you will be presented with projects in which you will learn how to successfully use the SHELL and EXEC instructions primarily in interactive console programmes.
You will have noticed that up to now no other possibilities have been presented for reading process data or permanently transferring inputs to an active process:
Events exist to signal you that data is available, but you can neither read this data directly nor send data to the process.These tasks are taken over by a Stream object, which was already presented in Chapter 15.2, since the Process class inherits from the Stream class.Therefore, you can treat a Process object like a Stream and use its input and output functions, such as Write, Print or Read.
The use of the event handler
are described and procedures are presented with which you pass input to a process and write input (data or commands) to the standard input of a process, respectively. The core of the procedures presented will be these statements:
Print #ProzessVariable Eingabe_String or Write #ProzessVariable Eingabe_String.
This project is about developing the basis for a graphical user interface (GUI) for the interactive console programme 'bc'.
Figure 21.3.3.1.1: Draft GUI for the programme 'bc' - Basic Calculator
There is at least one good reason to take a closer look at the programme 'bc':
hans@linux:~$ echo $(( 22/(7+4) )) 2 hans@linux:~$ echo $(( 22/(7+3) )) 2 hans@linux:~$ echo "scale=15; 0.77*2+e(3.44)" | bc -il scale=15; 0.77*2+e(3.44) 32.726958168309462 hans@linux:~$
In contrast to the integer arithmetic of a console, with the programme 'bc' you can, among other things, calculate with floating point numbers, use selected mathematical functions and also store and use self-defined constants and functions.
The programme Basic Calculator is a suitable candidate for demonstrating the use of the instructions SHELL and EXEC, because it can be shown how to
The source text is displayed in full and commented on in selected sections (A) to (F):
' Gambas class file Private $hProcess As Process Private sProgrammName As String = "bc" Private sStartParameter As String = "-i -l" Public Sub Form_Open() FMain.Center() FMain.Resizable = False txaOutput.Clear() btnProcessKill.Enabled = False txbEingabe.ReadOnly = True SetLEDColor("orange") End
(A)
The external programme is started by the instruction SHELL …. The process thus started is assigned to the process variable $hProcess and the event name is freely defined on myBCProcess.
Public Sub BCProcessStart() Dim sCommand As String txaOutput.SetFocus() txaOutput.Clear() txaOutput.Foreground = Color.Black $hProcess = Null sCommand = sProgrammName & " " & sStartParameter $hProcess = Shell sCommand For Read Write As "myBCProcess" FMain.Text = "BC - Basic Calculator : PID = " & $hProcess.Id btnBCProcessStart.Enabled = False btnProcessKill.Enabled = True txbEingabe.ReadOnly = False SetLEDColor("green") End
(B)
Data can be read from the process in the event handler myBCProcess_Read():
Public Sub myBCProcess_Read() Dim sPuffer As String txaOutput.Insert(gb.NewLine) sPuffer = "" Read #$hProcess, sPuffer, Lof($hProcess) txaOutput.Insert(sPuffer) End
The following alternatives are possible for 'Read #$hProcess, sPuffer, Lof($hProcess)':
Alternative 1:
Read #$hProcess, sPuffer, -256
Alternative 2:
While Not Eof($hProcess) Read #$hProcess, sPuffer, Lof($hProcess) Wend
Alternative 3:
Read #Last, sPuffer, Lof(Last)
The following instruction is not an alternative, although it is often quoted, because it does not fully read all incoming data on the standard output of the process:
Line Input #Last, sPuffer Line Input #$hProcess, sPuffer
The instruction LINE INPUT should only be used with caution in processes. LINE INPUT returns output of the process in units of “lines”, i.e. received data remains invisible to the Gambas programme until the line is terminated. But not every programme terminates important data with a NewLine character and therefore this information cannot be read out in the Gambas programme. For example, the shell sends a command prompt to signal that it is ready to receive a new command. This prompt is usually not terminated by a NewLine, which is why the programme - if it uses LINE INPUT - cannot read this prompt. The implied readiness of the shell to continue is lost.
(C)
The event Process_Error(…) has a special feature: You do not read directly from the standard error output of the process, but get the data already in the parameter of the event handler with Process_Error(sErrorMessage As String), if data are present at the standard error output of the process. Whether you output the error message unfiltered or still provide an error handling routine to influence the programme flow depending on the displayed error is certainly determined by the task to be solved:
Public Sub myBCProcess_Error(sFehler As String) txaOutput.Clear txaOutput.Insert(gb.NewLine) txaOutput.Insert("PROCESS FAILURE!" & gb.NewLine) txaOutput.Insert(gb.NewLine) txaOutput.Insert(sFehler) End
(D)
If user inputs are to be passed to the process, then you must realise this in a separate procedure. The programme 'bc' as a console programme expects arithmetic expressions and special commands in a line on the standard input of the started process. These inputs are provided in the main programme via an input box:
Public Sub txbEingabe_Activate() If txbEingabe.Text = "" Then Return txbEingabe.Text = Trim(txbEingabe.Text) WriteToMyBCProcess(txbEingabe.Text) txbEingabe.Clear End
You must take care in a separate procedure to write the inputs for the external program 'bc' into the standard input of the started process. Since you can treat a Process object like a stream, use its output functions Print or Write after checking whether a Process object exists and whether the process is active:
Public Sub WriteToMyBCProcess(sInput As String) If $hProcess Then If $hProcess.State = $hProcess.Running Then Print #$hProcess, sInput Endif Endif End
(E)
If you enter the command 'quit', then the external programme Basic Calculator is terminated in a regular manner and the process object is destroyed after the process was previously terminated. The triggered event Kill activates the event handler myBCProcess_Kill(), which gives you the opportunity to react to it appropriately:
Public Sub myBCProcess_Kill() txaOutput.Insert(gb.NewLine) txaOutput.Foreground = Color.Red txaOutput.Insert("Rückgabewert von '" & sProgrammName & "' = " & $hProcess.Value & Chr(10) Select Case $hProcess.State Case 0 txaOutput.Insert("Prozess (PID= " & $hProcess.Id & ") normal beendet." & gb.NewLine) Case 1 txaOutput.Insert("Prozess (PID= " & $hProcess.Id & ") gestoppt!" & gb.NewLine) Case 2 txaOutput.Insert("Prozess (PID= " & $hProcess.Id & ") beendet! (SIGKILL)" & gb.NewLine) End Select SetLEDColor("red") btnProcessKill.Enabled = False btnBCProcessStart.Enabled = True End
You get the following output in the presented project:
Return value from programme 'bc' = 0 The process with the PID = 7411 was terminated normally.
(F)
If you close the main programme, then you should first kill the started process with the method ProcessVariableName.Kill:
Public Sub btnProcessKill_Click() txaOutput.Clear() If $hProcess Then $hProcess.Kill() FMain.Text = "BC - Basic Calculator" End
Also in this case you will get an output generated in the event handler myBCProcess_Kill():
Return value from programme 'bc' = 9 The process with the PID = 7458 was terminated! (SIGKILL]
The rest of the source code does not contain any special features:
Public Sub btnClose_Click() FMain.Close End ' btnClose_Click() Public Sub btnTextAreaClear_Click() txaOutput.Clear End Public Sub btnBCProcessStart_Click() BCProcessStart() End Public Sub txaOutput_Change() txaOutput.Pos = Len(txaOutput.Text) ' ---> Sprung in die letzte Zeile End Public Sub SetLEDColor(sLEDColor As String) PictureBox1.Picture = Picture["LED/led_" & sLEDColor & ".svg"] End Public Sub Form_Close() If $hProcess Then txaOutput.SetFocus txaOutput.Clear $hProcess.Kill Endif ' $hProcess ? End
Hint:
It does not necessarily mean an error if data is output on the standard error output, i.e. the Error() event of a process is called. The programme “strace”, for example, takes all its output on stderr so that it can be distinguished from the output of the called programme by redirecting the streams. If one is only interested in the output of strace, then one can ignore everything else (>/dev/null or 2>strace.log).
Before you set off to develop your version from the presented approach to a GUI for the console program 'bc', a look at the BC help files with 'man bc' or 'info bc' will help.
This lists interesting sources to learn about the features and use of the Basic Calculator program:
Project