Shell Curses成立于1993年,旨在解决需要一组可移植的游标操纵函数的特定问题,而不必为每个新平台都重新编译它们。 这些功能最初是用Bourne Shell编写的,此后已迁移到Korn Shell 93,尽管当前的功能集在Bash中也能很好地工作。 自1993年以来,已经下载了超过200万的Shell Curses,现在全世界的组织都在各种应用程序中使用它。 作为Shell Curses的作者,我可以告诉您,当前版本是免费提供的,可以出于任何目的而未经许可使用。
功能
在15年前编写此功能集时,大多数UNIX®系统管理员还是C语言程序员,并且熟悉称为Curses的C语言功能库,该库提供了光标操作和文本窗口功能。 如今,由于图形界面的激增,许多UNIX系统管理员对光标操作和文本窗口并不熟悉。 尽管仍然需要这些功能,但是知识库似乎正在缩小。 本文的目的是提高人们对Shell Curses函数库的持续需求和存在的认识。
C语言Curses和Shell Curses函数库之间的区别在于C语言库是已编译的二进制文件,必须进行编译并将其链接到其他已编译的程序中。 Shell Curses函数库是许多Curses函数的纯Shell脚本实现,不需要编译。 Korn Shell 93版本的Shell Curses使用的唯一UNIX实用程序是“ tput
”。 有关tput的更多信息,请参见“ 相关主题”部分,因为本文不会对此进行讨论。 Shell Curses函数的名称和参数结构与已编译的Curses函数的名称和参数结构相同,因此,尽管Bourne或Korn Shell脚本语法与C语言有所不同,但是熟悉Curses的程序员也将熟悉如何使用Shell Curses。语言。
壳牌诅咒图书馆
Shell Curses库由按逻辑使用顺序排列的以下功能组成:
表1:标准Shell Curses功能
功能 | 争论 | 描述 |
---|---|---|
初始化 | 初始化shell curses屏幕寻址系统 | |
恩德温 | 取消初始化curses屏幕寻址系统 | |
| [BufferName] | 将逻辑缓冲区清空到屏幕上。 如果为该函数指定参数字符串,它将把存储在环境变量BufferName中的光标命令回显到屏幕。 |
明确 | 清除屏幕。 | |
移动 | $ {RowNbr} $ {ColNbr} | 将逻辑光标移动到指定的行和列。 需要行号和列号参数。 |
| $ {RowNbr} > $ {ColNbr} | 将物理光标移动到指定的行和列。 需要行号和列号参数。 |
加法 | X | 在屏幕上的当前位置打印字符。 需要一个字符参数。 |
addstr | $ {String} | 在屏幕上当前位置打印String的值。 |
瓦德奇 | $ {RowNbr} > $ {ColNbr} X | 将逻辑光标移动到指定的行和列。 在屏幕上指定行和列处打印指定字符。 需要行号和列号参数。 |
mvaddstr | $ {RowNbr} $ {ColNbr} $ {String} | 将逻辑光标移动到指定的行和列。 在屏幕上指定行和列的位置打印String的值。 需要行和列号参数,也需要String参数。 |
克豆油 | 清除当前行从当前列到行尾。 | |
机器人 | 从当前列清除屏幕到屏幕末尾。 | |
ch | 从标准输入中检索一个字符。 | |
getstr | 从标准输入中检索字符串。 | |
英施 | 在当前屏幕位置插入一个字符。 | |
姆文施 | $ {RowNbr} > $ {ColNbr} | 将逻辑光标移动到指定的行和列。 在RowNbr,ColNbr处插入字符 需要行号和列号参数。 |
插入 | 在当前屏幕位置插入一行。 | |
德尔奇 | 在当前屏幕位置删除一个字符。 | |
姆德尔奇 | $ {RowNbr} $ {ColNbr} | 将逻辑光标移动到指定的行和列。 在当前位置删除一个字符。 需要行号和列号参数。 |
删除素 | 删除当前行的行。 | |
attroff | 关闭所有屏幕属性。 | |
Attron | 什么也没做。 仅出于诅咒兼容性而存在。 | |
属性 | $ {Attribute} | 设置“属性”定义的屏幕属性。 需要属性字符串定义。 有效属性: 反向录像 闪烁闪烁模式 粗体粗体视频 昏暗的Half Bright视频 smul启动下划线模式 rmul结束下划线模式 sgr0退出所有属性 |
增强的Shell Curses功能
Shell Curses函数库中包含以下功能,这些功能在常规“ Curses”功能的基础上提供了增强的功能:
表2:增强的Shell Curses功能
功能 | 争论 | 描述 |
---|---|---|
savescr | [BufferName] | 将逻辑屏幕缓冲区保存到BufferName定义的环境变量中。 需要一个屏幕名称字符串。 |
氯乙烯醇 | $ {RowNbr} $ {ColNbr} | 将逻辑光标移动到指定的行和列。 需要行号和列号参数。 |
克拉波 | 清除从当前列到该行开头的行。 | |
mvclrtobol | $ {RowNbr} $ {ColNbr} | 将逻辑光标移动到指定的行和列。 清除从当前列到该行开头的行。 需要行号和列号参数。 |
机器人 | $ {RowNbr} $ {ColNbr} | 将逻辑光标移动到指定的行和列。 从当前列清除显示到显示结尾。 需要行号和列号参数。 |
w | 从标准输入中检索一个单词。 | |
嘟 | 敲响显示屏铃。 | |
chkparm | $ {String} | 确定“ String”的值是否为null,如果是,则返回false,否则返回true。 需要一个字母数字参数。 |
kin | $ {Nbr} | 确定“ Nbr”的值是否为数字,如果是,则返回true,否则返回false。 需要一个数字参数。 |
chklines | $ {Nbr} | 确定“ Nbr”的值是否小于或等于屏幕上的行数。 如果是这样,则返回true,否则返回false。 需要一个数字参数。 |
chkcols | $ {Nbr} | 确定Nbr的值是否小于或等于显示屏上的行数。 如果是这样,则返回true,否则返回false。 需要一个数字参数。 |
例子
由于长度限制,此处未提供这些功能的实际代码。 请参阅“ 相关主题”部分以获取下载信息。
使用Shell Curses创建了许多工具集,以提供安装系统,菜单程序,数据输入应用程序,数据库等的前端接口。 这里将提供一些示例,显示各种功能的使用。
在编写Shell脚本时,必须首先做出的决定之一是使用功能库还是将文件包含为包含所有必需功能的“点”脚本。 Shell Curses可以通过任何一种机制来使用,并且由Shell程序员来做出决定。 下面的示例将表示Shell Curses作为函数库的用法,因为这是此讨论最有效的机制。 作为函数库,假定每个函数作为一个单独的文件存在于目录中,该文件的名称与函数名称相同。 在这些示例中,用于Shell Curses函数库的目录是/usr/local/function/shellcurses
。 他们还使用Korn Shell 93作为脚本解释器,如每个脚本开头在shebang行( #!/usr/bin/ksh93
)上所定义。
在IBM®AIX®下使用Shell Curses函数库的一个问题是,它已经提供了名为clear
和refresh
已编译二进制文件,这些二进制文件将与具有相同名称的Shell函数混淆。 要解决此问题, clear
, refresh
和initscr
函数都应全部复制到函数库initscr
的单个文件中。 这样可以确保在调用initscr
函数时,同时初始化用于clear
和refresh
的Shell Curses函数,从而避免与AIX二进制命令clear
和refresh
混淆。
本示例使用Shell Curses函数清除屏幕。 将光标移至第10行第25列; 并显示“ Hello World!
”字样Hello World!
在那个位置。 然后将光标移到第23行第1列。注意对refresh
功能的调用。 该功能实际上将命令发送到要显示的屏幕。
#!/usr/bin/ksh93
FPATH="/usr/local/functions/shellcurses"
initscr
clear
move 10 25
addstr "Example 1: Hello World!"
move 23 1
refresh
endwin
exit 0
下一个示例与前面的示例相同,除了它使用单个mvaddstr
函数而不是两个函数( move
和addstr
:
#!/usr/bin/ksh93
FPATH="/usr/local/functions/shellcurses"
initscr
clear
mvaddstr 10 25 "Example 2: Hello World!"
move 23 1
refresh
endwin
exit 0
第三个示例提示用户输入一些数据,然后在屏幕下方的某个位置显示该数据。 请注意,提示包含在shell变量中,提示中的字符数用于确定在屏幕上放置诅咒的位置,以便用户输入数据。 还要注意,在提示用户输入之前,为了将命令刷新到屏幕,在getstr
函数之前调用了refresh
函数:
#!/usr/bin/ksh93
FPATH="/usr/local/functions/shellcurses"
initscr
clear
PROMPT="Example 3: Enter some data...:"
LEN="${#PROMPT}"
(( COL = LEN + 2 + 1 ))
mvaddstr 10 2 "${PROMPT}"
move 10 ${COL}
refresh
ANS=$( getstr )
mvaddstr 15 2 "Here is the data you entered.: ${ANS}"
move 23 1
refresh
endwin
exit 0
第四个示例提供了一个完整的菜单系统,该系统带有标题和对无效选择的错误检查。 做出选择后,它还会重画屏幕并显示用户的选择。
#!/usr/bin/ksh93
FPATH="/usr/local/functions/shellcurses"
SCRWID="80"
initscr
clear
#### Display a screen header, centered on the screen
HEADER="Shell Curses Example 4"
HDRWID="${#HEADER}"
(( HDRBEGIN = ( SCRWID - HDRWID ) / 2 ))
mvaddstr 1 ${HDRBEGIN} "${HEADER}"
MENU[0]="First Menu Line"
MENU[1]="Second Menu Line"
MENU[2]="Third Menu Line"
MENU[3]="Fourth Menu Line"
MENU[4]="Fifth Menu Line"
ITEMCNT="${#MENU[@]}"
MENUWID="${#MENU[0]}"
#### Determine the maximum length of the longest menu item
for MENULINE in "${MENU[@]}"
do
(( ${#MENULINE} > MENUWID )) && (( MENUWID = ${#MENULINE} + 4 ))
done
#### Display the numbered menu item, centered on the screen
(( COL = ( SCRWID - MENUWID ) / 2 ))
NBR="0"
for MENULINE in "${MENU[@]}"
do
mvaddstr $(( ++NBR + 4 )) ${COL} "${NBR} = ${MENULINE}"
done
#### Prompt the user for a selection
mvaddstr 22 2 "Enter the number of your selection: "
mvaddstr 23 2 "( 0 = Exit )"
#### Read the users selection from the screen
ANSWER="99"
while [[ "_${ANSWER}" != _[0-9] ]] ||
(( ANSWER < 1 )) ||
(( ANSWER > ITEMCNT ))
do
[[ "_${ANSWER}" == _0 ]] && exit 0
mvclrtoeol 22 40
refresh
ANSWER="$( getstr )"
done
#### Display the item number selected by the user
clear
mvaddstr 1 ${HDRBEGIN} "${HEADER}"
mvaddstr 4 2 "You selected menu item number ${ANSWER}"
move 23 1
refresh
endwin
exit ${ANSWER}
第五个示例提供了带有错误检查的完整多行数据输入系统。 此示例利用了动态确定的屏幕宽度和高度,还使用了反向视频屏幕属性来强调显示在屏幕上的错误消息。
#!/usr/bin/ksh93
FPATH="/usr/local/functions/shellcurses"
initscr
SCRWID="${MAX_COLS}"
SCRLEN="${MAX_LINES}"
clear
#### Display a two line header, centered on the screen
HEADER1="Shell Curses Example 5"
HEADER2="Data Entry Screen"
mvaddstr 1 $(( ( SCRWID - ${#HEADER1} ) / 2 )) "${HEADER1}"
mvaddstr 2 $(( ( SCRWID - ${#HEADER2} ) / 2 )) "${HEADER2}"
#### Define the user entry prompts
DOTS="........................................"
PRMPT[1]="Enter an existing directory name"
PRMPT[2]="Enter an existing regular file name"
PRMPT[3]="Enter an alpha-numeric value"
PRMPT[4]="Enter a numeric value"
PRMPT[5]="Enter an executable file name"
PCNT="${#PRMPT[@]}"
#### Display the numbered data entry prompt with trailing dots
for IDX in "${!PRMPT[@]}"
do
(( DOTWID = ( SCRWID / 2 ) - ( ${#PRMPT[IDX]} + 4 ) ))
PRMPT[IDX]="${PRMPT[IDX]}${DOTS:0:${DOTWID}}"
mvaddstr $(( IDX + 4 )) 2 "${IDX}: ${PRMPT[IDX]}...:"
done
DATACOL="$(( ${#PRMPT[1]} + 10 ))"
#### Prompt the user for a selection
mvaddstr $(( SCRLEN - 3 )) 2 "Select a line number to enter data:"
#### Read the users data line number selection from the screen
while :
do
#### Read the line number entered by the user
mvclrtoeol $(( SCRLEN - 3 )) 40
refresh
ANS="$( getstr )"
#### clear the status line and re-display the default status message
mvclrtoeol $(( SCRLEN - 2 )) 2
mvaddstr $(( SCRLEN - 2 )) 2 "( 0 = Exit )"
#### Validate the user entered line number, if invalid go back and ask again
[[ "_${ANS}" == "_0" ]] && exit 0
if [[ "_${ANS}" != _+([[:digit:]]) ]] || (( ANS < 1 )) || (( ANS > PCNT ))
then
continue
fi
#### Read the line data from the user entry
mvclrtoeol $(( ANS + 4 )) ${DATACOL}
refresh
DATA[${ANS}]="$( getstr )"
mvclrtoeol $(( SCRLEN - 2 )) 2
attrset rev
#### Check the validity of the line 1 data as entered by the user
if (( ANS == 1 )) && [[ ! -d "${DATA[${ANS}]}" ]]
then
mvclrtoeol $(( ANS + 4 )) ${DATACOL}
mvaddstr $(( SCRLEN - 2 )) 2 "ERROR: Invalid Directory Name"
fi
#### Check the validity of the line 2 data as entered by the user
if (( ANS == 2 )) && [[ ! -f "${DATA[${ANS}]}" ]]
then
mvclrtoeol $(( ANS + 4 )) ${DATACOL}
mvaddstr $(( SCRLEN - 2 )) 2 "ERROR: Invalid Regular File Name"
fi
#### Check the validity of the line 3 data as entered by the user
if (( ANS == 3 )) && [[ "_${DATA[${ANS}]}" != _+([[:alnum:]]) ]]
then
mvclrtoeol $(( ANS + 4 )) ${DATACOL}
mvaddstr $(( SCRLEN - 2 )) 2 "ERROR: Invalid Alpha-Numeric Value"
fi
#### Check the validity of the line 4 data as entered by the user
if (( ANS == 4 )) && [[ "_${DATA[${ANS}]}" != _+([[:digit:]]) ]]
then
mvclrtoeol $(( ANS + 4 )) ${DATACOL}
mvaddstr $(( SCRLEN - 2 )) 2 "ERROR: Invalid Numeric Value"
fi
#### Check the validity of the line 5 data as entered by the user
if (( ANS == 5 )) && [[ ! -x "${DATA[${ANS}]}" ]]
then
mvclrtoeol $(( ANS + 4 )) ${DATACOL}
mvaddstr $(( SCRLEN - 2 )) 2 "ERROR: File is not executable or
does not exist"
fi
attroff
refresh
done
endwin
exit ${ANS}
这些示例仅代表Shell Curses函数库的应用程序和用法的一小部分。 在法式菜单下存在一个基于Shell Curses的标准化菜单和数据输入系统。
该菜单系统使Shell程序员能够创建可以与各种程序和数据库进行交互的多级菜单系统和多屏幕数据输入程序。 如果未指定任何后端程序或数据库,则它将使用内置数据库系统来存储用户指定的信息。 提供了一个演示程序来说明这些功能。 French Menus还利用了Shell Curses库中的大多数(如果不是全部)功能,并且是设计和实现其他应用程序时很好的参考工具。
结论
本文的目的是强调对基于文本的应用程序和程序的持续需求,并提高对为此目的创建的现有工具的认识。 诸如Shell Curses和French Menus之类的功能库使基于文本的应用程序的创建更加容易,并提供了可用来评估和评估Shell编程的标准。 由于许多UNIX程序员都了解Shell Curses,或者至少了解Curses,因此基于这组函数编写Shell程序将使它们可支持,可扩展和可维护。
翻译自: https://www.ibm.com/developerworks/aix/library/au-shellcurses/index.html