*nix 的面向对象 shell

*nix 的面向对象 shell

前言:我喜欢 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用于FTP
  • mailMailmailx等用于基本电子邮件
  • 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

答案4

匆忙它使用红宝石和普什这是基于 perl 的。

相关内容