Gambas 脚本

介绍

Gambas Scripter是Gambas解释器的一个基于文本的前端。应用程序/脚本是可由任何Linux编辑器编辑的简单文本文件。这些脚本是需要程序员处理编写脚本而BASH不能提供复杂数据类型和数据处理的绝佳办法。操作系统识别Gambas脚本的mime类型的方式与识别BASH或DASH脚本的方式相同。

Scripter很好地集成到Linux shell环境中,通过 env[<env variable>] 可以直接访问shell环境变量。

Scripter不需要完整的Gambas IDE提供的复杂接口。事实上,使用Gambas脚本根本不需要IDE。

Scripter是BASH的一个很好的替代方案,可以快速实现复杂的系统管理过程,为程序员带来一种实现良好的全方位的编程语言。这允许脚本使用图形和文本基础界面。

Gambas脚本在编译时会进行优化,并且只有在脚本发生更改时才会缓存和重新编译编译编译后的脚本。这大大提高了运行Gambas脚本时的系统性能和响应能力。

Scripter还可以用于编译和执行更传统的Gambas项目。

自从 3.16

Scripter may also be used as a component to a Gambas Application allowing the application to dynamically load or create text based scripts which can be directly loaded and executed by the application. The loaded script will have access to the applications classes and public class variables.

Note:
  Throughout this document Scripter and gbs3 are used interchangeably.
  The actual linux executable is gbs3

Scripter - How it works

The Scripter(gbs3) works by: creating a temporary project from your script, compiling it, and running it.

自从 3.16

The Gambas Scripter gbs3 can also directly execute a project without the use of the IDE. The compiled project is cached so that it is executed immediately if you run the project again. The Scripter can also be used to generate a component which can be loaded and used at runtime.

Example: directly execute a script file
> gbs3 ~/myscripts/script1.gbs
> ./script1.gbs

自从 3.16

Example: directly execute a project
> gbs3 ~/myprojects/project1

Creating a Script

To create a Gambas script file, make it executable and write this preamble at the beginning:

#!/usr/bin/env gbs3

The Scripter can execute free-form programs with ease. To achieve this, Scripter will collect your free-form commands into a Main() subroutine automatically while maintaining the integrity of subroutines/structures/class separately.

#!/usr/bin/env gbs3

sub PrintBye()
    print "Bye"
end

dim sHello as string = "Hello"
Print sHello
PrintBye()

The Gambas script can also be written like a module in a common Gambas project, the interpreter automatically executes a Main() subroutine when included in the script.

TIP: Scripts execute more quickly if your code includes a Sub Main() subroutine rather than using the free-form method of writing a script. This is due to the Scripter not having to collect the free-form code and create a Main() subroutine.

#!/usr/bin/env gbs3

Public Sub Main()
    DoPrintHello()
End

Private Sub DoPrintHello()
    Print "Hello"
End

The code can be organized with private and public subs and functions, and constants and variables just like in Gambas modules inside Gambas projects.

Installing Scripter - gbs3

Scripter is not installed by default when the IDE is installed onto your system. It must explicitly be installed before it is available. It needs to be installed through your distributions package manager.
For example on Mint/Ubuntu the following command could be used
 sudo apt install gambas3-gb-scripter
See your distribution package manager for specifics.

To use Scripter it is not required to have the Gambas3 IDE installed.
Scripter can be installed standalone. When installed Scripter will cause the gambas-devel package containing the compiler linker and archiver to be installed and also the gb.pcre component if they are not already present on the system.

When installed standalone, the user will be required to individually install any components needed by executed scripts.

Defining Classes and Structures within a Gambas script

Unlike a Gambas project, Scripts support Classes and Structures in a script-specific way.

Classes are defined inline within your script code, structures must be public and defined before any reference to them in the script.

Defining a class inline

To define a class the following format is used:

 #!/usr/bin/env gbs3

 Public sMyVar1 as string

 Public oMyClass as new MyClass

 class MyClass
   sub sub1()
     print "hello"
   end
 end class

 oMyClass.sub1()

A Class's data and functions are defined in the same way as in the IDE except you must add the Class and End Class statement. Notice that the class does not have to be defined before it is used.

Defining a structure inline

Structures, which are a special case of class, must be defined before any reference to that structure. All structures must be declared as Public and are Global.

Examples of defining Structures

 #!/usr/bin/env gbs3
 Public sMyVar1 as string
 Public oMystruct as new MyStruct

 Public Struct MyStruct
  sText as string
 end struct

 oMystruct.sText = "hello"

The above example will fail!

The correct method is

 #!/usr/bin/env gbs3
 Public sMyVar1 as string
 public struct MyStruct
   sText as string
 end struct

 Public oMystruct as new MyStruct
 oMystruct.sText = "hello"

Executing a Gambas script

There are two ways to execute a Gambas script

You may create a non-executable file as in the example below.

From a file named MyScript.gbs containing:

Public Sub Main()
    DoPrintHello()
End

Private Sub DoPrintHello()
    Print "Hello"
End

This may be executed with:

gbs3 MyScript.gbs

An alternative to this, and most commonly used, is to provide the command inside the file and make it executable.
From the file myscript.gbs containing the following:

 #!/usr/bin/env gbs3

 Public Sub Main()
    DoPrintHello()
 End

 Private Sub DoPrintHello()
    Print "Hello"
 End

If the file is made executable with

chmod 755 myscript.gbs

Then it can be executed with the simple command:

./myscript.gbs

from the current directory. If we want to make this a command for general personal use it can be copied to:

 ~/bin

or for more general use to:

 /usr/bin

Passing Arguments To The Script

There is an ARGS[] array which contains arguments passed by command line.

#!/usr/bin/env gbs3

Dim arg as String
Dim i as Integer

for each arg in ARGS
    Print "Argument Nr. " & i & " = " & arg
    Inc i
next

See that position 0 of ARGS is used by Gambas itself, it contains the path to the cached compiled project:

$ ./test.gbs ding dong
Argument Nr. 0 = /tmp/gambas.1000/script-cache/b1826db433d3855de7e021ca9ad34b87/test.gbs
New for 3.15.3 Arg 0 now reports the name of the script correctly as test.gbs
Argument Nr. 1 = ding
Argument Nr. 2 = dong

Note: It is possible to get the name of your original script using file.name(args[0])

Using Scripter gbs3 as a component in your project

自从 3.16
It is possible to use Scripter gbs3 as a component in your project. Scripter provides three interfaces:

The first is for dynamically loading plugins and is defined as follows:

 '' This allows integration of scripter into an application that uses plugins
 '' the call would be ScripterPlugin(MyScript,"-b -c etc these are other parameters")
 '' This returns the instantiated class of the plugin script

 ScripterPlugin(ScriptPath As String, Optional CompileParameters As String[] = [],
                PluginNewParameters As Variant[] = Null) As Variant

 ScripterPlugin.fromString(ScriptName As String,ScriptText as string,
                           Optional CompileParameters As String[] = [],
                           PluginNewParameters As Variant[] = Null) As Variant

 ScripterPlugin.SetVerbose()                                                        'Turns on verbose information

The second allows the execution of a script from text and return its output

 ScripterExecute(ScriptName as string,ScriptText As String,
                 Optional CompileParameters As String[] = [],
                 PluginNewParameters As Variant[]) as variant

and the third is for executing a script directly returning the exit code and is defined as follows:

'' execute a script and return the exit code
'' aArgs[0] being the script name

Scriptmain.ScripterMain(aArgs As String[]) As Integer

Loading the gbs3 component

自从 3.16
To include gbs3 as a component in your application the following code is required

dim Scripter,ScriptMain,ScriptExecute as object
component.load(system.find("gbs3"))
Scripter = class.load("ScripterPlugin").new()
ScriptExecute = class.load("ScripterExecute").new()
Scriptmain = class.load("ScriptMain").new()

once this is done it is just a matter of calling the interface

dim myplugin1 as object = Scripter("~/myplugins/MyPlugin1")
myPlugin1()

dim myplugin2 as object = Scripter.fromString("MyClass","print \"Hello World\"")
myPlugin2()

or to execute a text script and get the output
dim result as variant
result = ScriptExecute("MyClass2","Print \"hello world\"\nPrint \"This is also returned\"")

or to execute a script by passing the command line parameters returning the exit code for example:

dim result as integer
result = ScriptMain.ScripterMain(["-v","-f","~/myscripts/MyScript1.gbs","Script parm1","Parm2"])

Creating Plugins using Scripter --plugin

自从 3.16

Scripter can create plugins that can be used by other scripts or Gambas applications after being built using the --plugin or -p option. The term plugin is synonymous with the term component throughout this document.

Plugins are created in a user-selected directory and are also executed from and cached in this directory, unlike a normal script, in which executables are created in the temp directory and are executed from there. This allows plugins to persist after the Scripter exits.

When a plugin script is cached it will not be recompiled Scripter will return with no error.

If no output destination is specified, then the plugin is created in the:
  ~/.local/lib/gambas directory.

Plugins may be loaded from there using the component.load() function.

Scripts being used as plugins are generated with the entry point defined as
      with a class of the same name as the basename of the script
      _Call(...) as variant
by default.

A Scripter generated plugin source may look like this for a plugin script

from:

print "hello"

to:

Class MyPlugin1
  public sub _Call(...) as variant
   print "hello"
  end
end class

So inside your script the parameters are accessed using the param gambas class. It is also possible to define the entry point yourself with fixed parameters.

_Call(counter as integer) as integer

from:

public sub _Call(count as integer) as integer
      print count
end

to:

class MyPlugin1
  public sub _Call(count as integer) as integer
      print count
  end
end class

Example: Building an App that uses plugins

自从 3.16

   Define some simple plugins/components for testing

   ~/myscripts/MyPlugin1.gbs:
      print "The Parameter was",param[0]
      return "This was done"

   ~/myscripts/MyPlugin2.gbs:
      print "Number of parameters passed=",param.count
      return "So was this"

   ~/myscripts/MyPlugin3.gbs:
     public sub _Call(counter as integer) as integer
       for i as integer = 0 to counter
         print i;
       next
       return i
     end

   Execute gbs3 to create a component plugin.
     > gbs3 --plugin ~/myscripts/MyPlugin1.gbs ~/myplugins
     > gbs3 --plugin ~/myscripts/MyPlugin2.gbs ~/myplugins
     > gbs3 --plugin ~/myscripts/MyPlugin3.gbs ~/myplugins

   Now in the script that requires the plugin, they can be loaded as in the following script:
     #!/usr/bin/env gbs3
     dim MyPlugin1,MyPlugin2,MyPlugin3 as object

     ' Make sure we have the latest version, assume all compiles are perfect..
     exec ["gbs3","--plugin","~/myscripts/MyPlugin1.gbs","~/myplugins"] 'if already in cache and no change then just returns
     exec ["gbs3","--plugin","~/myscripts/MyPlugin2.gbs","~/myplugins"]
     exec ["gbs3","--plugin","~/myscripts/MyPlugin3.gbs","~/myplugins"]

     ' Load the plugins
     component.load(User.home &/ "/myplugins/MyPlugin1")
     MyPlugin1 = class.load("MyPlugin1").new()
     component.load(User.home &/ "/myplugins/MyPlugin2")
     MyPlugin2 = class.load("MyPlugin2").new()
     component.load(User.home &/ "/myplugins/MyPlugin3")
     MyPlugin3 = class.load("MyPlugin3").new()

     ' Now lets access and call the plugin
     print MyPlugin1("some parameter")
     print MyPlugin2("one","two","three")
     print MyPlugin3(100)

This ability is most useful in an application which creates the script on the fly and then incorporates it into the application with access to all application classes.

    #!/usr/bin/env gbs3
    dim thenewone as object
    file.sav