![*nix 的面向对象 shell](https://linux22.com/image/42493/*nix%20%E7%9A%84%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%20shell.png)
前言:我喜欢 bash,无意发起任何形式的争论或圣战,希望这不是一个极其天真的问题。
这个问题有点相关这个帖子关于超级用户,但我认为OP并不真正知道他在要求什么。我在 FreeBSD、linux、OS X 上使用 bash,在 Windows 上使用 cygwin。最近,我在 Windows 上使用 PowerShell 也获得了丰富的经验。
是否有一个用于 *nix 的 shell(已经可用或正在开发中)与 bash 兼容,但在混合中添加了一层面向对象的脚本?我所知道的唯一接近的是 python 控制台,但据我所知它不提供对标准 shell 环境的访问。例如,我不能只在 python 控制台中使用cd ~
and ls
, then 。chmod +x file
我必须使用 python 而不是标准的 unix 二进制文件来执行这些任务,或者使用 python 代码调用二进制文件。
这样的外壳存在吗?
答案1
我可以想到 shell 中的三个理想功能:
- 交互可用性:常用命令应该能够快速输入;完成; ...
- 编程:数据结构;并发性(作业、管道……); ...
- 系统访问:处理文件、进程、窗口、数据库、系统配置……
Unix shell 倾向于专注于交互方面,并将大部分系统访问和一些编程分包给外部工具,例如:
- 公元前对于简单的数学
- 开放式SSL用于密码学
- sed,awk以及其他用于文本处理的
- 数控用于基本 TCP/IP 网络
ftp
用于FTPmail
、Mail
、mailx
等用于基本电子邮件cron
对于计划任务- 控制面板用于基本的 X 窗口操作
- 德科普对于 KDE ≤3.x 库
- 总线工具(
dbus-*
或总线)用于各种系统信息和配置任务(包括现代桌面环境,例如 KDE ≥4)
通过使用正确的参数或管道输入调用命令可以完成很多很多事情。这是一种非常强大的方法——每个任务都有一个工具可以很好地完成任务,这比一个单一的程序能完成所有任务但效果不佳更好——但它确实有其局限性。
unix shell 的一个主要限制,我怀疑这就是您所追求的“面向对象脚本”要求,即它们不擅长将信息从一个命令保留到下一个命令,或者以比一条管道。特别是,程序间通信是基于文本的,因此应用程序只有以兼容的方式序列化数据才能进行组合。这既是福也是祸:一切都是文本的方法使快速完成简单任务变得容易,但为更复杂的任务增加了障碍。
交互式可用性也与程序的可维护性背道而驰。交互式程序应该很短,需要很少的引用,不会因为变量声明或输入等而打扰你。可维护的程序应该是可读的(所以没有很多缩写),应该是可读的(所以你不必怀疑一个裸词是否是字符串、函数名、变量名等),应该进行一致性检查,例如变量声明和类型等。
总之,shell 是一个很难达成的妥协。好了,吐槽部分到此结束,进入示例。
这Perl 外壳 (psh)“将 Unix shell 的交互特性与 Perl 的强大功能相结合”。可以用 shell 语法输入简单的命令(甚至管道);其他一切都是 Perl。该项目已经开发了很长时间了。它是可用的,但还没有达到我考虑使用它而不是纯 Perl(用于脚本)或纯 shell(交互式或用于脚本)的程度。
Python是一个改进的交互式 Python 控制台,特别针对数值和并行计算。这是一个相对年轻的项目。
irb(交互式红宝石)是 Ruby 中与 Python 控制台对应的版本。
ssh是一种方案实现(即一种不错的编程语言),具有传统上在 UNIX shell 中找到的系统绑定类型(字符串、进程、文件)。然而,它的目的并不是用作交互式 shell。
桀骜是一个改进的交互式 shell。它的强项是交互性(命令行编辑、完成、使用简洁但神秘的语法完成的常见任务)。它的编程功能不是那么好(与 ksh 相当),但它附带了许多用于终端控制、正则表达式、网络等的库。
鱼是 Unix 风格 shell 的一个干净的开始。它没有更好的编程或系统访问功能。因为它破坏了与sh的兼容性,所以它有更多的空间来演化出更好的功能,但这并没有发生。
附录:unix 工具箱的另一部分将许多东西视为文件:
- 大多数硬件设备都可以作为文件访问。
- 在Linux下,
/sys
提供了更多的硬件和系统控制。 - 在许多 UNIX 变体上,可以通过文件系统完成进程控制
/proc
。 - 保险丝使得编写新的文件系统变得容易。已经存在用于动态转换文件格式、通过各种网络协议访问文件、查看档案内部等的现有文件系统。
也许unix shell的未来不是通过命令更好的系统访问(以及更好的控制结构来组合命令),而是通过文件系统更好的系统访问(它们的组合有些不同——我认为我们还没有弄清楚关键的习惯用法是什么(比如壳管)尚未)。
答案2
您不需要太多的 bash 代码来实现 bash 中的类或对象。
比如说 100 行。
Bash 具有关联数组,可用于实现具有继承、方法和属性的简单对象系统。
因此,您可能会定义一个这样的类:
class Queue N=10 add=q_add remove=q_remove
创建此队列的实例可以如下完成:
class Q:Queue N=100
或者
inst Q:Queue N=100
由于类是用数组实现的,班级和安装实际上是同义词——有点像 javascript 中的同义词。
将项目添加到该队列中可以像这样完成:
$Q add 1 2 aaa bbb "a string"
将项目删除到变量 X 中可以像这样完成:
$Q remove X
对象的转储结构可以这样完成:
$Q dump
这会返回类似这样的内容:
Q {
parent=Queue {
parent=ROOT {
this=ROOT
0=dispatch ROOT
}
class=Queue
N=10
add=q_add
remove=q_remove
0=dispatch Queue
}
class=Q
N=4
add=q_add
remove=q_remove
0=dispatch Q
1=
2=ccc ddd
3=
4=
}
类是使用类函数创建的,如下所示:
class(){
local _name="$1:" # append a : to handle case of class with no parent
printf "$FUNCNAME: %s\n" $_name
local _this _parent _p _key _val _members
_this=${_name%%:*} # get class name
_parent=${_name#*:} # get parent class name
_parent=${_parent/:/} # remove handy :
declare -g -A $_this # make class storage
[[ -n $_parent ]] && { # copy parent class members into this class
eval _members=\"\${!$_parent[*]}\" # get indices of members
for _key in $_members; do # inherit members from parent
eval _val=\"\${$_parent[$_key]}\" # get parent value
eval $_this[$_key]=\"$_val\" # set this member
done
}
shift 1
# overwrite with specific values for this object
ROOT_set $_this "$@" "0=dispatch $_this" "parent=${_parent:-ROOT}" "class=$_this"
}
注意:定义新类或实例时,您可以覆盖任何成员值或函数。
Bash 关联数组有一个怪癖,可以让这项工作变得简洁:$Q[0]} 与 $Q 相同。这意味着我们可以使用数组名称来调用方法调度函数:
dispatch(){
local _this=$1 _method=$2 _fn
shift 2
_fn="$_this[$_method]" # reference to method name
${!_fn} $_this "$@"
}
缺点是我不能使用 [0] 来获取数据,因此我的队列(在本例中)从索引 = 1 开始。或者我可以使用像“q+0”这样的关联索引。
到得到和放会员你可能会这样做:
# basic set and get for key-value members
ROOT_set(){ # $QOBJ set key=value
local _this=$1 _exp _key _val
shift
for _exp in "$@"; do
_key=${_exp%%=*}
_val="${_exp#*=}"
eval $_this[$_key]=\"$_val\"
done
}
ROOT_get(){ # $QOBJ get var=key
local _this=$1 _exp _var _key
shift
for _exp in "$@"; do
_var=${_exp%%=*}
_key=${_exp#*=}
eval $_var=\"\${$_this[$_key]}\"
done
}
并倾倒一个对象结构,我做了这个:
注意:这对于 bash 中的 OOP 来说不是必需的,但很高兴看到对象是如何创建的。
# dump any object
obj_dump(){ # obj_dump <object/class name>
local _this=$1 _j _val _key; local -i _tab=${2:-(${#_this}+2)} # add 2 for " {"
_tab+=2 # hanging indent from {
printf "%s {\n" $_this
eval "_key=\"\${!$_this[*]}\""
for _j in $_key; do # print all members
eval "_val=\"\${$_this[\$_j]}\""
case $_j in
# special treatment for parent
parent) printf "%*s%s=" $_tab "" $_j; ${!_val} dump $(( _tab+${#_j}+${#_val}+2 ));;
*) printf "%*s%s=%s\n" $_tab "" $_j "$_val";;
esac
done
(( _tab-=2 ))
printf "%*s}\n" $_tab ""
return 0
}
我的 OOP 设计没有考虑对象中的对象 - 除了继承的类。您可以单独创建它们,或者创建一个特殊的构造函数,例如 class()。 *obj_dump* 需要修改以检测内部类以递归地打印它们。
哦!我手动定义一个 ROOT 类来简化班级功能:
declare -gA ROOT=( \
[this]=ROOT \
[0]="dispatch ROOT" \
[dump]=obj_dump \
[set]="ROOT_set" \
[get]="ROOT_get" \
)
通过一些队列函数,我定义了一些类,如下所示:
class Queue \
in=0 out=0 N=10 \
dump=obj_dump \
add=q_add \
empty=q_empty \
full=q_full \
peek=q_peek \
remove=q_remove
class RoughQueue:Queue \
N=100 \
shove=q_shove \
head_drop=q_head_drop
创建了一些 Queue 实例并使其工作:
class Q:Queue N=1000
$Q add aaa bbb "ccc ddd"
$Q peek X
$Q remove X
printf "X=%s\n" "$X"
$Q remove X
printf "X=%s\n" "$X"
$Q remove X
printf "X=%s\n" "$X"
class R:RoughQueue N=3
$R shove aa bb cc dd ee ff gg hh ii jj
$R dump
答案3
ksh93t+ 引入了一些面向对象的概念,同时保留了 bourne/posix shell 语法:http://blog.fpmurphy.com/2010/05/ksh93-using-types-to-create-object-orientated-scripts.html