65.9K
CodeProject 正在变化。 阅读更多。
Home

用于生物信息学研究人员的强大 ShellScript

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2014 年 9 月 24 日

GPL3

16分钟阅读

viewsIcon

16378

downloadIcon

133

ShellScript 最初是为了调试我的“genome-in-code”虚拟细胞模拟引擎而开发的,但现在它已成为 .Net 程序的脚本语言。

 
下载 ShellScript 源代码

简介与背景

ShellScript 最初是为了调试我的 GCModeller 虚拟细胞模拟引擎而开发的,但现在它已成为 .Net 程序的脚本语言。由于我的 genome-in-code 虚拟细胞程序调试需要大量的命令行进行参数验证,因此调试这样一个庞大复杂的系统以查找错误是一项非常艰巨的工作。而系统命令行仅返回 0 或其他数字进行命令行调试。使用系统命令行进行调试非常不方便,因此我必须为我的程序调试开发一个脚本,这个想法催生了 ShellScript。现在,ShellScript 已成为我生物信息学研究的强大工具!

ShellScript 开发的目的是让一个简单的命令就能为研究人员完成大量出色的工作,因为所有的生物学研究人员都惧怕大量的复杂程序代码。

特点

  1. 支持 LINQ 脚本查询面向对象数据库
  2. 使用 Vb.NET/C#/F# 等 .NET 语言轻松开发 ShellScript 的 API
  3. 完全支持 Win32 API
  4. 让您的 .NET 程序可脚本化
  5. 支持 Microsoft .NET Bio
  6. 支持我的“genome-in-code”虚拟细胞项目中的 LANS.SystemsBiology 包。
  7. 支持并行计算和查询

一些 shellscript 示例,用于开始本文的介绍

这里使用一个简短的生物信息学脚本示例,使用 shellscript 通过与 circos 交互进行基因组图谱绘制,以引起您的注意

imports circos
imports assemblyfile.io

ptt <- read.ptt E:\Desktop\xcb_vcell\xc8004\xcb.ptt
circos <- session.create_new

export <- "x:\xc8004_genome_map\"

doc <- circos.generate_doc ptt $ptt myvacog "E:\Desktop\xcb_vcell\xcb_myvacog.csv"
call doc.export doc $doc export_to $export
call shell "perl E:\circos\bin\circos -conf x:\xc8004_genome_map\circos.conf"

circos 绘制输出,通过与 shellscript 交互绘制细菌 xcc8004 基因组

 

绘制遗传时钟图

imports diagram.genetic_clock

idlist <- array.new 2.1.1.71-RXN.XC_0035,2PGADEHYDRAT-RXN.XC_2531,2TRANSKETO-RXN.XC_0929,3.4.24.55-RXN.XC_2972,TRNA-NUCLEOTIDYLTRANSFERASE-RXN.XC_0960,1.2.1.32-RXN.XC_0104,1.2.1.67-RXN.XC_0366,1.2.1.9-RXN.XC_0972,1.2.7.8-RXN.XC_0179,1.5.1.15-RXN.XC_1933,1.5.1.20-RXN.XC_0326,1.5.1.20-RXN.XC_3496,1.7.2.2-RXN.XC_2177,1.7.7.2-RXN.XC_2175,1.8.1.4-RXN.XC_2751,1.8.1.4-RXN.XC_3689,1.8.4.8-RXN.XC_0990,1.8.4.8-RXN.XC_0991,1.8.4.8-RXN.XC_1334,1PFRUCTPHOSN-RXN.XC_1743

data <- read.serials "E:\GCModeller\CompiledAssembly\test\EnzymeActivity.csv"
data <- data.select data $data id $idlist
data <- data.interpolate data $data n 2

image <- diagram.createfrom data $data scale 1
call save.bitmap image $image saveto "x:\genetic clock example.bmp"

使用 shellscript“genome-in-code”系统生物学包为细菌 xcc8004 基因组的实验数据绘制遗传时钟图的示例

Shellscript helloworld 示例

hw <- 'hello world"
msgbox message $hw title "hi!,i'm the test example!"
call test newform $hw

那么,一个简单的 cowsay 技巧怎么样?

msg <- "Moo.. Hi! I'm a cow, but not a real cow, a text drawing cow!"
call $msg -> cowsay

哈哈,真有趣。

致谢

ShellScript 最初是基于 VisualBasic 的 Microsoft Windows 7 新功能培训资源中的命令行解释器源代码开发的。

shellscript 工作在一个命令行解释器上,该命令行解释器定义在 Microsoft.VisualBasic.CommandLine 命名空间下,最初来源于 Microsoft Windows 7 培训资源,我对该命令行解释器进行了进一步开发,现在它比 Microsoft 开发者所做的具有更多高级功能。

** 本文中我用作示例的生物信息学 shellscript API 库(我为系统生物学研究人员开发的 genome-in-code 项目库)由于科学论文尚未发表以及仍需大量实验室实验验证,因此暂不对外发布。我仍在努力进行实验室实验验证这些包。

 

使用代码

命令行解释器介绍

所有新的命令行解释器功能都依赖于 Microsoft.VisualBasic.CommandLine.Reflection 命名空间中的自定义属性

''' <summary>
''' Use for the detail description for a specific commandline switch.(用于对某一个命令的开关参数的具体描述帮助信息)
''' </summary>
''' <remarks></remarks>
<AttributeUsage(AttributeTargets.Method, allowmultiple:=True, inherited:=True)>
Public Class SwitchDescription : Inherits Attribute
  
''' <summary>
''' A command object that with a specific name.
''' (一个具有特定名称命令执行对象)
''' </summary>
''' <remarks></remarks>
<AttributeUsage(AttributeTargets.Method, allowmultiple:=False, inherited:=True)>
Public Class CommandAttribute : Inherits Attribute

您可以通过以下两步轻松实现命令行解释器

  1. 定义命令行委托处理程序

您只需应用一个命令属性,然后填写描述属性,等等,除了名称是必需的,其他属性都是可选的,这让您的工作更轻松。

如果您希望您的程序命令行的帮助信息更有帮助,那么您应该在函数上应用 switchdescription 自定义属性,并且 switchdescription 属性也是可选的。

这里是命令行解释器的详细实现示例。

''' <summary>
''' Using the regular expression to search the motif pattern on a target nucleotide sequence.(使用正则表达式搜索目标序列)
''' </summary>
''' <param name="argvs"></param>
''' <returns></returns>
''' <remarks></remarks>
<Command("-pattern_search", info:="Parsing the sequence segment from the sequence source using regular expression.",
 usage:="-pattern_search -i <file_name> -p <regex_pattern>[ -o <output_directory> -f <format:fsa/gbk>]",
 example:="-pattern_search -i ~/xcc8004.txt -p TTA{3}N{1,2} -f fsa")>
<SwitchDescription(name:="-i", optional:=False,
 description:="The sequence input data source file, it can be a fasta or genbank file.",
 example:="~/Desktop/xcc8004.txt")>
<SwitchDescription(name:="-p", optional:=False,
 description:="This switch specific the regular expression pattern for search the sequence segment,\n" &
              "for more detail information about the regular expression please read the user manual.",
 example:="N{1,5}TA")>
<SwitchDescription(name:="-o", optional:=True,
 description:="Optional, this switch value specific the output directory for the result data, default is user Desktop folder.",
 example:="~/Documents/")>
<SwitchDescription(name:="-f", optional:=True,
 description:="Optional, specific the input file format for the sequence reader, default value is FASTA sequence file.\n" &
              " fsa - The input sequence data file is a FASTA format file;\n" &
              " gbk - The input sequence data file is a NCBI genbank flat file.",
 example:="fsa")>
Public Shared Function PatternSearchA(argvs As Microsoft.VisualBasic.CommandLine.CommandLine) As Integer
   Dim Format As String = argvs("-f")
   Dim Input As String = argvs("-i")
   Dim OutputFolder As String = argvs("-o")
   Dim FASTA As LANS.SystemsBiology.Assembly.SequenceModel.FASTA.File
   Dim pattern As String = argvs("-p").Replace("N", "[ATGCU]")

正如您所见,现在从用户输入的命令行获取开关参数变得非常简单。如果用户为特定开关指定了值,则您可以使用 CommandLine 的默认属性读取该值;如果用户未指定值,则默认属性将返回一个空字符串,不用担心,命令行解释器不会崩溃。

  1. 在您的程序的 Main 子程序中声明一个命令行解释器

完成此操作后,您可以在程序的 Main 子程序中初始化一个命令行解释器

Module Program

   Public Function Main() As Integer
       Dim strCommandLine As String = Command()

       If Not String.IsNullOrEmpty(strCommandLine) Then
           Dim CommandLine = Microsoft.VisualBasic.CommandLine.CommandLine.TryParse(strCommandLine)
           Return New CommandLine.Interpreter(GetType(CommandLines)).Execute(CommandLine)
       Else
           Call Program.ScriptShell()
       End If

       Return 0
   End Function
End Module

看起来我们已经清晰了控制台程序架构:如果用户指定了命令行来启动我们的程序,那么命令行解释器将处理该命令行;如果没有,则我们的程序可以执行其他操作。

手动文档自动生成

命令行语法

<Program> man

在您的程序中实现命令行解释器后,您就可以使用命令行中的 man 命令来显示程序的文档信息,并且该文档页面是根据您的命令行属性自动生成的。

帮助系统

命令行语法

<Program> ? [CommandName]

您可以使用 ? 命令获取您在命令行解释器中定义的子命令的帮助信息

如果您只指定一个 ? 字符,那么命令行解释器将列出您在命令行解释器中定义的所有可用命令

如果您在帮助“?”字符后面指定一个 commandName 字符串并用空格隔开,那么您就可以获得有关目标命令的更详细的帮助信息。

工作原理

与编译脚本不同(脚本文本 -> 对象模型 -> 动态程序集):ShellScript 的运行方式与大多数解释型脚本语言相同(脚本文本 -> 数据模型 -> 分配委托处理器)

  • 将脚本文件解析为命令令牌
  • 将函数指针处理器添加到命令令牌
  • 通过函数指针处理器执行 API
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

这两张图展示了编译脚本和解释脚本之间的区别,关于编译脚本工作机制的详细信息,您可以查阅我之前的一篇文章:

[^] https://codeproject.org.cn/Articles/721827/LINQ-Script-A-Universal-Object-Oriented-Database-Q

Using the Code

面向开发者:如何从 VB.NET/C# 创建 ShellScript 的 API

本 ShellScript API 声明将使用两个自定义属性(Namespace 和 Command 属性类,来自 Namespace Microsoft.VisualBasic.CommandLine.Reflection)

''' <summary>
''' (CommandLine interpreter executation Entry and the ShellScript software packages namespace.)这是一个命令行解释器所使用的执行入口点的集合
''' </summary>
''' <remarks></remarks>
<AttributeUsage(AttributeTargets.Class, allowmultiple:=False, inherited:=True)>
Public Class [Namespace] : Inherits Attribute
    Dim _Namespace As String

    Public ReadOnly Property [Namespace] As String
       Get
          Return _Namespace
       End Get
    End Property

    Sub New([Namespace] As String)
       _Namespace = [Namespace]
    End Sub

    Private Shared ReadOnly _TypeInfo As System.Type = GetType([Namespace])
    Public Shared ReadOnly Property TypeInfo As System.Type
       Get
           Return _TypeInfo
       End Get
    End Property

    Public Overrides Function ToString() As String
       Return String.Format("Namespace {0}", _Namespace)
    End Function

    ''' <summary>
    ''' 从目标类型之中构造出一个命令行解释器
    ''' </summary>
    ''' <param name="Type"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function CreateInstance(Type As System.Type) As Microsoft.VisualBasic.CommandLine.Interpreter
       Return New Microsoft.VisualBasic.CommandLine.Interpreter(Type)
    End Function
End Class

''' <summary>
''' A command object that with a specific name.
''' (一个具有特定名称命令执行对象)
''' </summary>
''' <remarks></remarks>
<AttributeUsage(AttributeTargets.Method, allowmultiple:=False, inherited:=True)>
Public Class CommandAttribute : Inherits Attribute

   Dim _Name As String

   ''' <summary>
   ''' 这个命令的名称
   ''' </summary>
   ''' <value></value>
   ''' <returns></returns>
   ''' <remarks></remarks>
   Public ReadOnly Property Name As String
      Get
          Return _Name
      End Get
   End Property


   ''' <summary>
   ''' Something detail of help information.(详细的帮助信息)
   ''' </summary>
   ''' <value></value>
   ''' <returns></returns>
   ''' <remarks></remarks>
   Public Property Info As String

   ''' <summary>
   ''' The usage of this command.(这个命令的用法,本属性仅仅是一个助记符,当用户没有编写任何的使用方法信息的时候才会使用本属性的值)
   ''' </summary>
   ''' <value></value>
   ''' <returns></returns>
   ''' <remarks></remarks>
   Public Property Usage As String

   ''' <summary>
   ''' A example that to using this command.(对这个命令的使用示例,本属性仅仅是一个助记符,当用户没有编写任何示例信息的时候才会使用本属性的值)
   ''' </summary>
   ''' <value></value>
   ''' <returns></returns>
   ''' <remarks></remarks>
   Public Property Example As String

   Sub New(Name As String)
      _Name = Name
   End Sub

   Public Overrides Function ToString() As String
      Return Name
   End Function

   Private Shared ReadOnly _TypeInfo = GetType(CommandAttribute)
   Public Shared ReadOnly Property TypeInfo As System.Type
      Get
          Return _TypeInfo
      End Get
   End Property
End Class
  • 命名空间声明

命名空间表示一组有用的命令,命名空间属性只能应用于类类型的声明,示例如下:

<[Namespace]("gcmodeller.engine_kernel.experiment_system")>
Public Class ShellScriptAPI
  • 命令声明

Command 是实现特定功能的函数,它应该应用于已应用了命名空间自定义属性的类的共享函数;下面是用 VisualBasic 语言创建 ShellScript API 的简单示例

<[Namespace]("gcmodeller.engine_kernel.experiment_system")>
Public Class ShellScriptAPI

    Friend Shared EngineKernel As EngineSystem.Engine.Modeller

    <Command("get.current_time")>
    Public Shared Function Get_currentTime() As Integer
        Return EngineKernel.get_RTime
    End Function

    <Command("build.dynamic.metabolism")>
    Public Shared Function DrawMetabolism() As Microsoft.VisualBasic.ComponentModel.Collection.Generic.KeyValuePairObject(Of Component(), ComponentInteraction())
        Return New DataVisualization.DynamicMap.DynamicMapBuilder(EngineKernel.KernelModule).ExportMetabolismNetwork
    End Function

    <Command("write.network")>
    Public Shared Function SaveNetwork(network As Microsoft.VisualBasic.ComponentModel.Collection.Generic.KeyValuePairObject(Of Component(), ComponentInteraction()), saveto As String) As Boolean
        Dim NetworkCsv As String = String.Format("{0}/Network.csv", saveto)
        Dim NodesCsv As String = String.Format("{0}/ComponentNodes.csv", saveto)

        Call network.Value.SaveTo(NetworkCsv, True)
        Call network.Key.SaveTo(NodesCsv, False)

        Return True
    End Function

    <Command("get.current_time.equals")>
    Public Shared Function CurrentTimeIs(value As Integer) As Boolean
        Return value = EngineKernel.get_RTime
    End Function
End Class

编译您的 API 代码后,您就可以在 ShellScript 系统中使用您创建的 API 了。现在,在 ShellScript 控制台中输入“?”帮助命令,您将看到声明结果(在系统控制台中输入“? gcmodeller.engine_kernel.experiment_system”)

我不建议 shell script 用户尝试使用这种脚本语言来构建大型复杂的程序来完成建模工作,因为这种 shell script 语言不是面向对象的。但我建议开发人员可以使用 VisualBasic.NET 和 C# 语言类库开发 shell script 模块包,并将您的 API 以格式良好的文档和清晰的接口导出到这种 shell script 语言。然后,用户就可以使用这种 shell script 来构建一个简单而智能的工具来完成研究工作。

API 创建要点

  1. 使用 namespacecommand 属性指定类类型和共享方法,以将 API 导出到 shellscript
  2. 使用 shellscript -register_modules 命令注册您的编译后的 .NET 程序集文件,您可以使用 ? 帮助命令找到详细的命令行
  3. 使用 shellscript 脚本化您的 API

将 ShellScript 集成到您的 .NET 程序中,使其可脚本化

如何将 ShellScript 嵌入到您的 .NET 程序中,非常简单,只需一个简单的声明语句

Dim _EmbeddedScriptEngine As Microsoft.VisualBasic.ShellScript.ShellScript = New ShellScript.ShellScript

然后您就可以通过一个简单的命令在程序中执行脚本了

Call _EmbeddedScriptEngine.EXEC(ScriptText)

这里是 EXEC 函数的定义:

''' <summary>
''' Execute a shellscript file
''' </summary>
''' <param name="ShellScript">
''' This script data should be a line of the script code that of which is user type input from the console terminal
''' or a text file content from the return value of <see cref="FileIO.FileSystem.ReadAllText"></see>
''' </param>
''' <returns></returns>
''' <remarks></remarks>
Public Function EXEC(ShellScript As String) As Integer
    If String.IsNullOrEmpty(ShellScript.Replace(vbCrLf, "").Trim) Then
         Return -1
    Else
#If Not Debug Then
     Try
#End If
         Dim ObjectModel = _Interpreter.CreateObject(ShellScript)
         Call ObjectModel.Execute(HostMemory:=Me._HostMemory)

#If Not Debug Then
     Catch ex As Exception
         Dim OriginalColor = Console.ForegroundColor

         Console.ForegroundColor = ConsoleColor.Red
         Console.WriteLine(vbCrLf & ex.ToString & vbCrLf)
         Console.ForegroundColor = OriginalColor

         Return -1
     End Try
#End If
     Return 0
   End If
End Function

如您所见,EXEC 函数有两个返回值,0 表示 ShellScript 执行无错误,-1 表示执行 ShellScript 时发生异常。

调用 win32 API

ShellScript 还可以用于系统管理,因为它的特点是完全支持 win32 API。下面是一个在 ShellScript 插件中的 win32 API 开发示例

Imports Microsoft.VisualBasic.CommandLine.Reflection

<[Namespace]("winmm.dll")>Public Class WinMM

    <ImportsConstant> Public Const SND_APPLICATION = &H80 ' look for application specific association
    <ImportsConstant> Public Const SND_ALIAS = &H10000 ' name is a WIN.INI [sounds] entry
    <ImportsConstant> Public Const SND_ALIAS_ID = &H110000 ' name is a WIN.INI [sounds] entry identifier
    <ImportsConstant> Public Const SND_ASYNC = &H1 ' play asynchronously
    <ImportsConstant> Public Const SND_FILENAME = &H20000 ' name is a file name
    <ImportsConstant> Public Const SND_LOOP = &H8 ' loop the sound until next sndPlaySound
    <ImportsConstant> Public Const SND_MEMORY = &H4 ' lpszSoundName points to a memory file
    <ImportsConstant> Public Const SND_NODEFAULT = &H2 ' silence not default, if sound not found
    <ImportsConstant> Public Const SND_NOSTOP = &H10 ' don't stop any currently playing sound
    <ImportsConstant> Public Const SND_NOWAIT = &H2000 ' don't wait if the driver is busy
    <ImportsConstant> Public Const SND_PURGE = &H40 ' purge non-static events for task
    <ImportsConstant> Public Const SND_RESOURCE = &H40004 ' name is a resource name or atom
    <ImportsConstant> Public Const SND_SYNC = &H0 ' play synchronously (default)

    <Command("playsoundA")>
    Public Declare Function PlaySound Lib "winmm.dll" Alias "PlaySoundA" (lpszName As String, hModule As Integer, dwFlags As Integer) As Integer
End Class

将此类编译成 DLL 文件并注册到 shell script 注册表中后,您就可以在脚本文件中像这样调用该 win32 API:

library ./win32api.dll
imports winmm.dll

filename <- "E:\舞随光动.dat"
call winmm.dll playsounda lpszname $filename hmodule 0 dwflags &snd_filename

对吧?这是一个播放 wav 文件的多媒体 API。而 WinMM 插件类只是 C/C++ 标准动态库和 ShellScript 文件之间的中介接口!

ShellScript 语法

Shellscript 中的命令语句基本由什么组成?

CommandType

语法

示例

值分配

<variableName> <- <commandLine>

A <- b

调用方法

[Call] <CommandLine>

Call 命令

CommandLine = [Namespace] <commandName> [switch_values]

这些示例向您展示了如何编写正确的命令行

[call] winmm.dll playsounda lpszname $filename hmodule 0 dwflags &snd_filename

语法令牌 call 是命令行的前缀,它表明该命令不会将任何值赋给变量,就像 VisualBasic 语言中的 call 关键字一样,它可以省略,每个 VisualBasic 程序员都知道这个语法属性。

然后第二个令牌是 winmm.dll,这是一个命名空间指示符,它表明下一个语法令牌 playsounda 来自一个名为 winmm.dll 的程序集模块,并且所有字符都可以用作此语法令牌的标识符,但空格和系统保留字符 $(表示标识符是内存中的变量)和 &(表示标识符是内存中的常量)(但实际上您可以在标识符中使用 $ 和 & 字符,但它们不能出现在标识符的第一个字符)。

然后最后的语法令牌 lpszname $filename hmodule 0 dwflags &snd_filename 是 playsounda 命令的参数,它们可以转换为键值对

KeyName(ParameterName)

Value(ParameterValue)

描述

lpszname

$filename

这是 shell script 内存中的变量引用

hmodule

0

这是一个用户定义的常量

dwflags

&snd_filename

这是来自 imports 命令的常量引用。

Shellscript 中的标识符规则

identifier 是 shellscript 中的一种基本元素,它包含 shellscript 中的变量名、命名空间和方法命令名。简而言之,shellscript 标识符不能以 $ 或 & 字符开头,然后任何其他字符在标识符中都是合法的,这与我们所知的任何其他编程语言都大不相同。

示例

解释器如何看待它

12345%$%#_*()sfsdds

它是一个标识符“12345%$%#_*()sfsdds”

12345% $%#_*()sfsdds

标识符是 12345% 并且其命令或函数参数是 $%#_*()sfsdds(注意,示例中 % 和 $ 之间有一个空格

$12345%$%#_*()sfsdds

这是我内存中的一个变量,但我找不到这个对象,所以我抛出了一个对象未找到的异常

&12345%$%#_*()sfsdds

这是我内存中的一个常量变量,但我找不到这个对象,所以我抛出了一个对象未找到的异常

注意:由于 shellscript 使用 <- 进行值分配,使用 -> 调用函数作为扩展方法,因此运算符 <--> 不允许在标识符中使用。

变量赋值前缀字符

$  is for all of the normal variables, excepts the imports constants from your API library

& is only for the imports constants which is come from the shellscript api library that you've developed

*  is for the function pointer which is the delegate function that we've create in the shellscript

[重要!!!] 向 shellscript 中的 API 传递参数规则

对于新手来说,从脚本行到 .NET 程序集 API 的参数传递在我的 shellscript 中有些奇怪,但对于熟悉它的人来说很容易学习,只需遵循 3 条规则即可

  • API 函数参数可以是 shellscript 内存中的变量引用,也可以是直接的常量值。
  • 通常,由于命令行解释器的工作机制(我在上一节中已提到),每个参数值都需要一个开关名称才能从命令中读取。因此,在将参数传递给函数时,每个参数名称都需要对应参数值。但如果 API 函数只有一个参数,则您可以直接从 shellscript 将参数传递给 API 函数,而无需指定任何其他参数名称。shellscript 支持可选参数。示例如下: 
if the function have more than one parameter:
function_name switch_name1 parametervalue1 switch_name2 parametervalue2....

if the function have just one parameter:
function_name parametervalue
  • 由于 VisualBasic 语言是强类型语言,而 shellscript 是由 VisualBasic 开发的,因此参数值的对象类型应与 shellscript 函数参数匹配。

ShellScript 中的控制结构

ShellScript 没有语法缩进,脚本由多行命令行组成,我更喜欢称 shell script 为类汇编语言的脚本。那么它有多少种控制结构呢?

下表列出了 ShellScript 中所有可用的结构控制元素

元素

语法

描述

示例

 

     

If

If test <booleanValue> call <commandline>

如果测试条件为真,则调用命令行;如果命令行有返回值,则返回的值将赋给左侧的变量

Data <- if test $condition call “read.txt $file“

Else

Else <Statement/Expression>

else 命令与前面的 if 命令连续。如果 if test 条件为真,则 else 命令不会执行;否则,如果 if 命令未执行,则 else 命令将执行

Data <- else “read.txt $another_file”

For.Each

For.each in <array> call <commandline>

这是 shellscript 的 for each 结构,commandline 是一个脚本文件或临时脚本行作为命令源,每个源脚本都有一个默认的变量名数组名,其值是目标数组对象中某个元素的值

Result <- for.each in $paths call “source ./test.txt”

 

./test.txt 有一个默认变量名为“paths”

Do.While

 

 

 

返回

Return <value>

此命令用于调用脚本返回一个特定值给其调用脚本

Return $data

所以这里有一个简短的示例,说明如何在 ShellScript 中使用这些控制结构元素

File <- “test.txt”
Another_file <- “test2.txt”

condition <- TRUE

data <- If test $condition call “read.txt $file”
data <- else “read.txt $another_file”
data <- split text $data delimiter #
data <- for.each in $data call “source ./test3.txt”

在 ShellScript 中创建委托方法的语法

<delegate_name> <- *
{
   <ShellScript command statements>
}

委托是 VisualBasic 和 C# 语言中最强大的特性,ShellScript 也可以创建委托函数。

如何声明委托函数

声明委托名称:委托名称声明应以 ShellScript 标识符开头,然后是标识符右侧的值分配运算符,然后在命令位置是字符 *,表示以下 ShellScript 命令语句是委托函数声明

每个委托声明体都应该用一对大括号括起来。

下面是我用 Cytoscape 实时绘制虚拟细胞动态代谢网络图像的委托示例

imports gcmodeller.engine_kernel.experiment_system

draw.map <- *
{
  network <- build.dynamic.metabolism
  rtime <- get.current_time
  argvs <- array.new $rtime

  file <- string.format expression "x:\metabolism_{0}_loops.csv" argvs $argvs
  call write.network network $network saveto $file
 
  *free network
}

flag <- get.current_time.equals 5
if test $flag call draw.map

从示例中我们可以看到,“draw.map <- *”这行脚本是声明委托名称的语句。然后后面的委托体被一对大括号括起来。这是 Cytoscape 输出的图像,它是我“genome-in-code”虚拟细胞实时计算中从 ShellScript 调用产生的。来自自然微生物的代谢网络不是很美吗?这个 shellscript 中的委托对于我复杂的系统调试操作和数据可视化非常有帮助!

使用委托函数绘制实时细胞系统网络图像的 shellscript 输出结果

 

扩展方法

ShellScript 就像 vb.net 和 c# 一样支持扩展方法,扩展方法可以让你在不修改类源代码的情况下扩展对象功能;为你的编码带来极大的便利。在 VisualBasic 中,每个扩展方法都应该声明在一个模块中,并且方法应该有一个 Extension 自定义属性。在 visualbasic 中创建扩展方法如下:

<Command("string.format")>
<Extension>
Public Function Format(Expression As String, argvs As Object()) As String
   Return String.Format(Expression, argvs)
End Function

然后像这样调用这个扩展方法:

Dim b = “456”
Dim a = “fjsdffrr”
Dim array = {a,b}
Dim Msg As String = “{0} is not {1}”.format(array)

ShellScript 也有扩展方法语法,但与 visualbasic 不同的是,您无需为函数添加扩展属性,这意味着您可以直接将方法视为其第一个参数的扩展方法。

这里是扩展方法语法

<variableName>/Value -> <method calling>

ShellScript 使用运算符“->”来调用对象的成员方法,就像 R 脚本一样。

例如,ShellScript 命令 API 定义如下:

Public Function Format(Expression As String, argvs As Object()) As String

函数的第一个参数是 Expression,它是字符串类型,因此您可以在 shellscript 中以扩展方法的方式调用此 API

Msg <- "{0} is not {1}"-> string.format argvs $array

这里是使用扩展方法方式的完整示例

$  b <- 456
$  a <- fjsdffrr
$  array <- "$a,$b" -> array.new
$  memory

    4 VARIABLES

b       --> 456  //System.String
a       --> asfdaasd  //System.String
array   --> System.Object[]  //System.Object[]

$  msg <- "{0} is not {1}"->string.format argvs $array
$  $msg
   = [0]  asfdaasd is not 456

$  call $msg -> msgbox title 12345

ShellScript 程序命令行

ShellScript 程序有三个命令行,您可以使用“shellscript man”获取用户手册文档

从 shellscript 命令行解释器输出的 Man 命令

Microsoft VisualBasic ShellScript(*.vbss) Host [vbss脚本执行引擎] [version 2.2.0.1]
Module AssemblyName: ShellScript.exe
Root namespace: Microsoft.VisualBasic.ShellScript

All of the command that available in this program has been list below:

 -register_linq_modules:  register the linq module into the linq script engine type registry
 -register_modules:  Register the shellscript API module assembly DLL or assembly exe file to the shellscript type registry.

Commands
--------------------------------------------------------------------------------

1.  Help for command '-register_linq_modules':
  Information:  register the linq module into the linq script engine type registry
  Usage:        ShellScript -register_linq_modules -path <assemnly_dll_file>
  Example:      ShellScript -register_linq_modules

2.  Help for command '-register_modules':
  Information:  Register the shellscript API module assembly DLL or assembly exe file to the shellscript type registry.
  Usage:        ShellScript -register_modules -path <assemnly_dll_file> [-module_name <string_name>]
  Example:      ShellScript -register_modules

  Switches information:
   ---------------------------------------
    -path
    Description:  the assembly file path of the API module that you are going to register in the shellscript type library
    Example:      -path ""

   [-module_name]
    Description:  The module name for the register type namespace, if the target assembly just have one shellscript namespace, then this switch value will override the namespace attribute value if the value of this switch is not null, when there are more than one shellscript namespace was declared in the module, then this switch opetion will be disabled.
    Example:      -module_name ""

有一些重要的命令您应该记住

  • Library: 在您的脚本中注册外部程序集模块

当您要使用程序集文件中的命令时,该程序集文件应先在 ShellScript 注册表中注册,否则将抛出程序集模块未找到的异常。使用命令行注册模块很好,但这并不能让您的脚本文件在发布给用户时更加方便。因此,您应该在使用程序集模块中的命令之前使用此 library 命令,因为它可以在运行时将程序集模块文件注册到注册表中。

Library <DLL/EXE relative path>
Call <commands>
  • Libraries: 列出所有已注册的程序集模块

libraries 命令可以枚举 ShellScript 注册表中所有已注册的程序集模块。如果您已注册任何其他模块到 ShellScript,则可以使用此命令查看概览

  • Imports: 命名空间导入命令

系统内置命令在调用这些命令时不需要任何命名空间前缀,但用户命令需要。在导入命名空间之前,每次使用外部用户命令时,都应该在命令名称令牌前加上命名空间前缀。但在导入命名空间后,您可以直接调用命令而无需任何命名空间前缀,这与 VisualBasic 语言中的 imports 关键字功能相同。

  • ?: 帮助命令

? 是 ShellScript 中的帮助命令,它有一个参数,该参数可以是方法名称或程序集模块命名空间

如果您使用命令“? <method name>”,ShellScript 将打印特定方法命令的详细帮助信息,前提是 ShellScript 能够找到如何将其加载到内存中。

如果您使用命令“? <module namespace>”,ShellScript 将打印开发人员在程序中编写的所有 shell 命令

gcmodeller.compiler 命名空间(来自我的 genome-in-code 项目)的帮助示例

help 命令列出了 gcmodeller.compiler 命名空间中所有可用的命令方法。方法入口列是您可以在脚本中调用的命令名称,返回类型列是目标命令的返回系统。

这只是一个命名空间的概览,然后您可以继续使用命令“? <namespace> <commandname>”来查看命令的详细信息

gcmodeller.compiler 命名空间中 invoke.compile 方法的帮助示例

<Command("Invoke.compile", info:="invoke the compiler compile method to compile the data into a virtual cell model file.",
     usage:="invoke.compile compiler $compiler argvs $argv_string",
     example:="call gcmodeller.compiler invoke.compile compiler $compiler argvs $argv_string")>
Public Shared Function Compile(compiler As LANS.SystemsBiology.Assembly.DocumentFormat.CsvTabular.Compiler.Compiler, argvs As String) As DocumentFormat.CsvTabular.FileStream.CellSystem
    Call compiler.Compile(argvs)
    Return compiler.Return
End Function

所有的帮助信息都源自命令的自定义属性,如您在命令定义示例中所见。

  • Source: 用于调用另一个脚本文件并获取其返回值的 source 命令
syntax
source <script_file_path> [switch1 parametervalue1 switch2 parametervalue2 ...]
  • Q(): 退出程序

您可能已经注意到 ShellScript 的语法有些类似于 R 语言,R 使用 q() 来退出脚本环境,因此 ShellScript 也使用 q() 来退出程序。

© . All rights reserved.