4. 添加功能

提供多个按钮来控制单个应用程序可能有点矫枉过正,为每个操作调用单独的程序也是如此。第三个问题是 apachectl 会将消息打印到标准输出,以指示命令是如何执行的。可以通过包含一个文本部件来显示 apachectl 的输出来改进应用程序。

在以下脚本中,我们将通过修改 screen 过程来重新设计应用程序,以使用单选按钮选择器和单个按钮,并在新框架中构建文本部件。我们还移除了 start、stop 和 restart 过程,并创建了 2 个新过程。第一个过程 init 将处理由单选按钮选择创建的条件,第二个过程 put_text 将启动 Apache 并将 apachectl 输出打印到文本部件

#!/usr/bin/wish

set apachectl "/usr/local/apache_new/bin/apachectl"

proc screen {} {
  frame .top -borderwidth 10
  pack .top -fill x
  radiobutton .top.start -text "start" -variable mode -value start
  radiobutton .top.stop  -text "stop" -variable mode -value stop
  radiobutton .top.restart -text "restart" -variable mode -value restart
  button .top.submit -text execute -command init 
  pack .top.start .top.stop .top.restart .top.submit -side left -padx 0p -pady 0 -anchor n
  frame .bottom
  pack .bottom -fill x
  text .bottom.main -relief sunken -bd 2 -yscrollcommand ".bottom.scroll set"
  scrollbar .bottom.scroll -command ".bottom.main yview"
  pack .bottom.main -side left -fill y
  pack .bottom.scroll -side right -fill y
}

proc init { } {
 global mode action
 switch $mode {
    stop     {set action "stop"}
    restart  {set action "restart"}
    default  {set action "start"}
  }
	put_text
}

proc put_text {} {
  global action apachectl
  set f [ open "| $apachectl $action" r]
  while {[gets $f x] >= 0} {
    .bottom.main insert 1.0 "$x\n"
  }
  catch {close $f}
}
screen

首先,让我们看一下 screen 过程。radiobutton 命令的工作方式与 html 单选按钮非常相似。-variable 参数接受变量名称作为参数。-value 参数接受变量的值作为参数。按钮 .top.submit 使用 -command 参数来调用稍后在脚本中定义的 init 过程。这些按钮随后被打包到顶部框架中,并创建了第二个名为 bottom 的框架。

底部框架由一个文本部件和一个滚动条组成。文本部件是使用 text 命令创建的,该命令接受各种选项。在本例中,我们使用了 -relief 选项,该选项指定字段的 3D 效果(-relief 的其他值包括 raised、flat、ridge、solid、groove);-bd 选项,该选项指定边框宽度;以及 yscrollcommand,该选项指定将由文本字段使用的滚动条的名称。我们的滚动条部件接受一个选项 -command,该选项指定当文本滚动超出与其交互的文本部件的屏幕时如何表现。

init 过程使用 global 命令将 mode 变量加载到其本地命名空间中,并使用 switch 语句来设置全局变量 action 的值。

在本例中,switch 命令测试 "$mode" 是否与列表中每行中的第一个单词匹配,并执行每行第二个单词中指定的操作。默认值在列表底部指定,并定义在未找到匹配项时执行的操作。Switch 接受 4 个选项:-exact,它需要区分大小写的匹配;-glob,它使用 glob 样式的模式匹配;-regexp,它使用正则表达式样式的匹配;以及 --,它指示选项的结束,通常在要匹配的模式带有 "-" 前缀时使用。

注意:我们可以使用 if-elseif-else 条件链而不是 switch 语句

if { $mode == "stop" } {
  set action "stop"
} elseif { $mode == "restart" } {
  set action "restart"
} else {
  set action "start"
}

init 过程做的最后一件事是调用 put_text 过程。

put_text 过程读取在 init 过程中设置的 action 的值,使用 action 指定的适当参数执行 apachectl,并将 apache 的输出打印到 .bottom.main 文本部件。

proc put_text {} {
 	global action apachectl
 	set f [ open "| $apachectl $action" r]
	while {[gets $f x] >= 0} {
 		.bottom.main insert 1.0 "$x\n"
	}
}

put_text 过程引入了 3 个新命令

首先,它将变量 f 的值设置为 open 命令的输出。Open 可用于打开文件、管道流或串口,并返回一个标识符,该标识符可用于读取、写入或关闭流。由于 open 之后的第一个字符是管道符号 "|",因此 $apachectl $action 被视为命令,并像给出了 exec 一样执行。r 指定流为只读。其他参数如下

r read only
r+  read and write if file exists
w write only
w+  read and write if file exists
a write only.  Create new file if none exists.
a+  read and write. Create new file if none exists.

第二个新命令是 while。While 是一个典型的 while 循环,只要满足指定条件,它就会执行一段参数体。在本例中,while 将读取一行输入并将其保存到变量 x 中,直到没有剩余内容可读取为止。insert 命令将每行输入插入到 .bottom.main 文本部件的第 1 行(1.0)的第零个字符处。