* nix的面向对象的外壳


38

前言:我喜欢bash,无意发动任何形式的争论或圣战,希望这不是一个非常幼稚的问题。

这个问题与有关超级用户的帖子有些相关,但是我不认为OP真正了解他的要求。我在FreeBSD,Linux,OS X和Windows上的cygwin上使用bash。我最近在Windows上使用PowerShell也有丰富的经验。

* nix的外壳是否已经存在或正在开发中,并且与bash兼容,但是在混合中添加了一层面向对象的脚本编制?我所知道的唯一接近的是python控制台,但据我所知它并不提供对标准Shell环境的访问。例如,我不能只是cd ~ls,而是chmod +x file在python控制台中。我将不得不使用python来执行那些任务,而不是标准的unix二进制文件,或者使用python代码调用二进制文件。

是否存在这样的外壳?


3
Pash,但是比Powerash更加像Bash。
短暂

1
@ephemient也许您应该为pash写一个答案...尽管我一无所知,iirc,powershell是一个OO shell。
xenoterracide 2010年

4
嘿,您应该查看ipython。如果输入的表达式没有python的意义,它将尝试将其映射到shell命令。例如,诸如此类的东西cd ~随后ls在Bash中起作用。您还可以使用类似命令将输出分配给Python变量(行列表等)listing = !ls
直觉

@intuited:太好了,我会检查一下
Robert S Ciaccio 2010年

1
@intuited:iPython对于我想做的事情一直很好,谢谢!
Robert S Ciaccio 2011年

Answers:


43

我可以想到Shell中的三个理想功能:

  • 交互式可用性:常见命令应该快速键入;完成 ...
  • 编程:数据结构;并发(工作,管道等);...
  • 系统访问:使用文件,进程,窗口,数据库,系统配置,...

Unix shell倾向于集中在交互方面,并将大多数系统访问权和某些编程分包给外部工具,例如:

  • 公元前简单数学
  • 用于加密的openssl
  • sedawk等用于文本处理
  • nc用于基本的TCP / IP网络
  • ftp 用于FTP
  • mailMailmailx,等基本的电子邮件
  • cron 用于计划任务
  • wmctrl用于基本的X窗口操作
  • 适用于KDE≤3.x库的dcop
  • dbus工具(dbus-*qdbus),用于各种系统信息和配置任务(包括现代桌面环境,例如KDE≥4)

通过调用带有正确参数或管道输入的命令可以完成许多事情。这是一种非常强大的方法-每个任务最好都有一个工具来完成它,而不是一个只有一个程序才能做得很好-但它确实有其局限性。

unix shell的一个主要限制,我怀疑这是您对“面向对象脚本”要求的追求,因为它们不擅长将信息从一个命令传递给下一个命令,或者不善于以比管道。尤其是,程序间的通信是基于文本的,因此,只有在应用程序以兼容的方式序列化其数据时,才可以对其进行组合。这既是祝福,也是诅咒:“一切皆有文字”的方法使快速完成简单任务变得容易,但为更复杂的任务增加了障碍。

交互式可用性还与程序可维护性背道而驰。交互式程序应该简短,几乎不需要引号,不要用变量声明或键入等来打扰您。可维护程序应该是可读的(因此没有太多缩写),应该是可读的(因此,您不必怀疑是否有裸词是一个字符串,一个函数名,一个变量名等),应进行一致性检查,例如变量声明和类型等。

总之,shell是很难达到的折衷方案。好的,这到示例结束rant部分。


  • Perl的壳牌(PSH) “结合了Unix外壳的互动性用Perl的力量”。可以使用Shell语法输入简单的命令(甚至管道)。其他一切都是Perl。该项目没有开发很长时间了。它是可用的,但还没有达到我考虑在纯Perl(用于脚本)或纯Shell(交互式或用于脚本)上使用它的地步。

  • IPython是一种改进的交互式Python控制台,特别针对数字和并行计算。这是一个相对较年轻的项目。

  • irb(交互式红宝石)是与Python控制台等效的Ruby。

  • scsh是一种方案实现(即,不错的编程语言),具有通常在unix shell(字符串,进程,文件)中发现的那种系统绑定。但是,它并不旨在用作交互式外壳。

  • zsh是一种改进的交互式外壳。它的强项是交互性(命令行版本,完成功能,使用简洁但隐晦的语法完成的常见任务)。它的编程功能不是很好(与ksh相当),但是它带有许多用于终端控制,正则表达式,联网等的库。

  • fish是unix式外壳的干净起点。它没有更好的编程或系统访问功能。因为它破坏了与sh的兼容性,所以它有更多的空间来发展更好的功能,但这没有发生。


附录:unix工具箱的另一部分将许多内容视为文件:

  • 大多数硬件设备都可以作为文件访问。
  • 在Linux下,/sys提供了更多的硬件和系统控制。
  • 在许多UNIX变体上,可以通过/proc文件系统完成过程控制。
  • FUSE使编写新文件系统变得容易。已经存在用于实时转换文件格式,通过各种网络协议访问文件,查看存档等的文件系统。

也许unix shell的未来不是更好的通过命令进行系统访问(以及更好的控制结构来组合命令),而是更好的通过文件系统进行系统访问(结合起来有所不同-我认为我们没有弄清关键用法(例如外壳管道))。


1
在您说“我怀疑这是您所追求的”之后,您立即打在了头上。我问这个问题的主要原因是,我喜欢拥有unix工具的强大功能,但是程序之间基于文本的交互无疑“为更复杂的任务增加了障碍”。我花了足够的编程时间来编写文本解析器:)我认为这是一个经过深思熟虑的答案。它成为问题的核心和主题的复杂性。我希望我能
投票

1
ipython +1,尽管我不知道OP想要做什么。
Falmarri 2010年

1
这是一个很好的答案:老实说,我认为有趣的博士学位论文的种子就在这里。
Ziggy,2012年

1
@RobertSCiaccio这个答案刚刚在最近的帖子中链接,您对文本解析的评论让我开始思考...如果文本解析足以完成您的“复杂任务”,那么您不能拥有一个小型脚本或程序来实现并将其用作bash脚本中的某种功能?只是想一想,我没有太多可以说的bash脚本编写经验。
Oxwivi 2014年

1
@onlyanegg用什么方式可以说鱼是“面向对象的”?鱼的主要目的是简化。有什么方法比其他方法更强大?
吉尔(Gilles)'所以

13

您不需要很多bash代码即可在bash中实现类或对象。

说100行。

Bash具有关联数组,可用于实现具有继承,方法和属性的简单对象系统。

因此,您可能会定义这样的类:

class Queue N=10 add=q_add remove=q_remove

创建此Queue的实例可以这样完成:

class Q:Queue N=100

要么

inst Q:Queue N=100

由于类是通过数组实现的,因此classinst实际上是同义词-类似于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]来存储数据,因此我的队列(在这种情况下)从index = 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

可能工作,但是很难看。完全不是bash为了什么。让我想起了斯蒂芬(Stephane)关于为什么不使用shell循环来处理文本的答案,尤其是“概念上”的小节,其中详细介绍了C和两种语言之间的目的差异bashunix.stackexchange.com/a/169765/135943
通配符

1
可以工作吗?它确实有效,但是您的意思是,尽管没有错,但只是没有完成。我也没有回答OP问题,但是如果bash是TC,那么我认为它应该能够处理对象。许多已经证明了这一点。
philcolbourn 2015年


5

IPython使用起来非常方便。

标准的Shell功能:作业控制,readline编辑和历史记录,别名cat ls cd以及pwd,分页器集成,通过在任何系统命令前加上- !或enable(%rehashx可分配给python变量的命令输出)来运行任何系统命令,python值可用作shell变量。

特定于Python:重用上一命令的结果,快速访问文档和源代码,重新加载模块,调试器。如果您愿意的话,可以使用一些集群支持。

也就是说,运行复杂的管道不是用Python完成的;您还将使用posix shell,仅需使用一些胶水即可将值来回传递。




2

这是一个更易于使用和设置的名称,它名为args等 。https://github.com/uudruid74/bashTheObjects

我用一个示例更新我的答案,该示例遵循为另一个答案提供的基本示例之一,但使用此语法。示例程序与之类似,但是您不必在所有变量前加上类名(它知道该类方法所示),而且我认为语法简单得多!

首先,一个类文件。实例变量的默认值是可选的,并且仅当您不将这些值传递给构造函数时才使用。

class Person
    public show
    public set
    public Name
    public Age
    public Sex
    inst var Name "Saranyan"
    inst var Age 10
    inst var Sex "Male"

Person::Person { :; }
Person::set() { :; }
Person::Name() { println $Name }
Person::Age() { println $Age }
Person::Sex() { println $Sex }
Person::show() {
    Person::Name
    Person::Age
    Person::Sex
}

现在举个例子:

#!/bin/bash
source static/oop.lib.sh

import Person

new Person Christy Name:"Christy" Age:21 Sex:"female"
new Person Evan Name:"Evan" Age:41 Sex:"male"

println "$(Evan.Name) is a $(Evan.Sex) aged $(Evan.Age)"
println "$(Christy.Name) is a $(Christy.Sex) aged $(Christy.Age)"
println "Stats for Evan ..."
Evan.show

assert 'kindof Person Evan'
assert '[ $Evan = $Evan ]'
assert 'kindof Person Christy'
assert '[ $Evan = $Christy ]'

笔记:

  1. 最后一个断言将失败。与上述示例不同,该库尚不支持对象分配,但是添加起来并不难。我将其与即将到来的容器/迭代器支持一起放在待办事项中。

从技术上讲,不需要import语句,但它会在给定的位置强制加载类,而不是等待第一个new加载,从而可以按正确的顺序初始化事物。请注意,一次即可设置多个实例变量的简便性。

还有调试级别,构造函数,析构函数,子类和基本反射系统,并显示了用print / println代替echo(是否尝试打印以破折号开头的变量?)。github上的示例显示了它作为CGI运行,并从类生成HTML。

库本身(oop.lib.sh)并不是那么简单(400行以上,11K),但是您只包含它而忘了它。



1

如果有人只想要面向对象编程的基础知识(属性和方法),那么,一个真正简单的框架就可以解决问题。

假设您要使用对象显示文本“ Hello World”。首先,创建一个对象类,该对象类具有要显示的文本的属性,并具有一些设置和显示该文本的方法。为了显示一个类的多个实例如何协同工作,我添加了两种显示文本的方法:一种在末尾带有NewLine,而另一种则没有。

类定义文件:EchoClass.class

# Define properties
<<InstanceName>>_EchoString="Default text for <<InstanceName>>"

# Define methods
function <<InstanceName>>_SetEchoString()
{
  <<InstanceName>>_EchoString=$1
}

function <<InstanceName>>_Echo()
{
  # The -ne parameter tells echo not to add a NewLine at the end (No Enter)
  echo -ne "$<<InstanceName>>_EchoString"
}

function <<InstanceName>>_EchoNL()
{
  echo "$<<InstanceName>>_EchoString"
}

请注意单词“ <<InstanceName>>”。稍后将替换它以创建一个类对象的多个实例。在使用对象的实例之前,需要一个实际创建它的函数。为了简单起见,它将使用一个单独的脚本:ObjectFramework.lib

# 1st parameter : object instance name
# 2nd parameter : object instance class

function CreateObject()
{
  local InstanceName=$1
  local ObjectClass=$2
  # We will replace all occurences of the text "<<InstanceName>>" in the class file 
  # to the value of the InstanceName variable and store it in a temporary file
  local SedString='s/<<InstanceName>>/'$InstanceName'/g '$ObjectClass'.class'
  local TmpFile=$ObjectClass'_'$InstanceName'.tmp'
  sed $SedString > $TmpFile

  # The file will contain code which defines variables (properties) and functions (methods)
  # with the name we gave to our object instance via the 1st parameter of this function
  # ... we run this code so the variables and functions are actually defined in runtime
  source "$TmpFile"

  # Than remove the temp file as we don't need it any more
  rm "$TmpFile"
}

因此,现在我们有了一个类定义文件和一个CreateObject函数,该函数创建该文件的副本,并将文本“ <<InstanceName>>”替换为所需的名称。

让我们在名为HelloWorld.sh的脚本中使用新对象 (请注意,HelloWorld.sh应该是可执行的。其他两个文件不需要)

# Define the CreateObject function via the lib file we created
source ObjectFramework.lib

# Create two instances of the EchoClass class
CreateObject MyHello EchoClass
CreateObject MyWorld EchoClass

# Call the SetEchoString method of the two objects. In reality these are 
# just two identical functions named differently and setting different
# variables (remember the <<InstanceName>>_EchoString variable?)
MyHello_SetEchoString "Hello "
MyWorld_SetEchoString "World"

# Finally we call the Echo and EchoNL (NewLine) methods
MyHello_Echo
MyWorld_EchoNL

通过运行HelloWorld.sh脚本,它将显示文本“ Hello World”(并添加换行符)。这个结果不会给任何人留下深刻的印象,但是我们知道这并不是那么简单,因为它看起来像:)

编码愉快!


使复杂的事物变得简单胜于使简单的事物变得复杂。
2016年

1

这是一个基于Python的面向对象的shell,但是它与Golang的关系接近:https : //github.com/alexst07/shell-plus-plus

例如,尝试catch:

try {
  git clone git@github.com:alexst07/shell-plus-plus.git
} catch InvalidCmdException as ex {
  print("git not installed [msg: ", ex, "]")
}

类和运算符重载:

class Complex {
  func __init__(r, i) {
    this.r = r
    this.i = i
  }

  func __add__(n) {
    return Complex(n.r + this.r, n.i + this.i)
  }

  func __sub__(n) {
    return Complex(n.r - this.r, n.i - this.i)
  }

  func __print__() {
    return string(this.r) + " + " + string(this.i) + "i"
  }
}

c1 = Complex(2, 3)
c2 = Complex(1, 2)
c = c1 + c2

print(c)

您可以使用类似bash的命令:

echo "Test" | cat # simple pipeline
ls src* | grep -e "test" # using glob

# using variables content as command
cip = "ipconfig"
cgrep = ["grep", "-e", "10\..*"]
${cip} | $@{cgrep} # pass an array to command

0

现在,大多数时候您在外壳中处理哪些对象?它是文件/目录,进程及其交互。所以它应该喜欢f1.edit或类似的东西currentFile=f1.c ; .edit ; .compile ; .run。或者d1.search(filename='*.c' string='int \*')。或者p1.stopp1.bg。这就是我对卵壳的理解。


0
## implemantion of base class
function Class()
{
    base=${FUNCNAME}
    this=${1}
    Class_setCUUID $this
    for method in $(compgen -A function)
    do
        export ${method/#$base\_/$this\_}="${method} ${this}"
    done

}

function copyCUUID()
{
        export ${2}_CUUID=$(echo $(eval "echo \$${1}_CUUID"))

}

function Class_setCUUID()
{
        export ${1}_CUUID=$(uuid)
}

function Class_getCUUID()
{
        echo $(eval "echo \$${2}_CUUID")
}


function Class_setProperty()
{
        export ${1}_${2}=${3}
}

function Class_getProperty()
{
        echo $(eval "echo \$${1}_${2}")
}

function Class_Method()
{
        echo "function ${1}_${2}()
        {
        echo null
        }
        " > /tmp/t.func
        . /tmp/t.func
        rm /tmp/t.func


}

function Class_setMethod()
{
        export ${1}_${2}=${1}_${2}
}


function Class_getMethod()
{
        $(eval "echo \$${1}_${2}")
}


function Class_equals()
{
        base="Class"
        this=${2}

    copyCUUID ${1} ${2}
    for method in $(compgen -A function)
    do
        export ${method/#$base\_/$this\_}="${method} ${1}"
    done


}

刚刚尝试根据参考http://hipersayanx.blogspot.in/2012/12/object-directional-programming-in-bash.html将oo概念引入bash

source ./oobash

Class person
$person_setProperty Name "Saranyan"
$person_setProperty Age 10
$person_setProperty Sex "Male"
function person_show()
{
$person_getProperty Name
$person_getProperty Age
$person_getProperty Sex
}
$person_setMethod show

$person_equals person1
$person1_getMethod show
$person1_equals person3
$person_getCUUID person
$person_getCUUID person1
$person_getCUUID person3

0

对不起,简短的答复,但是可以。

hipersayanx 在Bash中创建了一篇文章面向对象编程。基本上,他喜抬高$FUNCNAMEfunctioncompgen,并export创造尽可能接近OOP可以在bash获得。

最酷的部分是它运行良好,只需要几行样板即可构建一个类。

需要的基本部分是:

ClassName() {
# A pointer to this Class. (2)
base=$FUNCNAME
this=$1

# Inherited classes (optional).
export ${this}_inherits="Class1 Class2 Class3" # (3.1)
 for class in $(eval "echo \$${this}_inherits")
do
    for property in $(compgen -A variable ${class}_)
    do
        export ${property/#$class\_/$this\_}="${property}" # (3.2)
    done

    for method in $(compgen -A function ${class}_)
    do
        export ${method/#$class\_/$this\_}="${method} ${this}"
    done
done

# Declare Properties.
export ${this}_x=$2
export ${this}_y=$3
export ${this}_z=$4

# Declare methods.
for method in $(compgen -A function); do
    export ${method/#$base\_/$this\_}="${method} ${this}"
done
}

function ClassName_MethodName()
{
#base is where the magic happens, its what holds the class name
base=$(expr "$FUNCNAME" : '\([a-zA-Z][a-zA-Z0-9]*\)')
this=$1

x=$(eval "echo \$${this}_x")

echo "$this ($x)"
}

用法:

# Create a new Class Instance
ClassName 'instanceName' $param1 $param2

$instanceName_method

现在,我已在自己的AuditOps项目中使用了此功能,hipersayanx在他的网站上进一步了解了它的实际工作原理。票价警告尽管是非常严格的bashism,但不适用于bash 4.0之前的任何版本,并且可能导致调试麻烦。就我个人而言,我希望看到大部分锅炉重做本身都是一类。

当更适合您的项目时,使用严肃的OOP脚本语言(如perl,ruby和python)总是明智的。但是,以我的诚实选择,在维护模块化bash脚本以在bash中使用这种OOP方法时,值得花费时间和精力。


0

我在GitHub上开发了一个功能类似于HashMap Objectshell_map的函数

为了创建“ HashMap实例 ”,该功能可以使用不同的名称创建其自身的副本。每个新函数副本将具有不同的$ FUNCNAME变量。然后,将$ FUNCNAME用于为每个Map实例创建一个名称空间。

映射键是全局变量,格式为$ FUNCNAME_DATA_ $ KEY,其中$ KEY是添加到映射的键。这些变量是动态变量

在下面,我将其简化版作为示例。

#!/bin/bash

shell_map () {
    local METHOD="$1"

    case $METHOD in
    new)
        local NEW_MAP="$2"

        # loads shell_map function declaration
        test -n "$(declare -f shell_map)" || return

        # declares in the Global Scope a copy of shell_map, under a new name.
        eval "${_/shell_map/$2}"
    ;;
    put)
        local KEY="$2"  
        local VALUE="$3"

        # declares a variable in the global scope
        eval ${FUNCNAME}_DATA_${KEY}='$VALUE'
    ;;
    get)
        local KEY="$2"
        local VALUE="${FUNCNAME}_DATA_${KEY}"
        echo "${!VALUE}"
    ;;
    keys)
        declare | grep -Po "(?<=${FUNCNAME}_DATA_)\w+((?=\=))"
    ;;
    name)
        echo $FUNCNAME
    ;;
    contains_key)
        local KEY="$2"
        compgen -v ${FUNCNAME}_DATA_${KEY} > /dev/null && return 0 || return 1
    ;;
    clear_all)
        while read var; do  
            unset $var
        done < <(compgen -v ${FUNCNAME}_DATA_)
    ;;
    remove)
        local KEY="$2"
        unset ${FUNCNAME}_DATA_${KEY}
    ;;
    size)
        compgen -v ${FUNCNAME}_DATA_${KEY} | wc -l
    ;;
    *)
        echo "unsupported operation '$1'."
        return 1
    ;;
    esac
}

用法:

shell_map new credit
credit put Mary 100
credit put John 200
for customer in `credit keys`; do 
    value=`credit get $customer`       
    echo "customer $customer has $value"
done
credit contains "Mary" && echo "Mary has credit!"

您似乎误解了命令替换的工作方式。它由反引号中包含的命令输出代替,而不是由return status代替。换句话说,您的代码没有执行您认为的操作。
通配符

啊哈 你是对的。我很抱歉。但是,使用它会更加清晰carp "Some error message"; return
通配符

[ -z "$KEY" ] && { carp "some message"; return;} 不需要子shell。但是实际上,这看起来像是if ... elif ... elif ... else ... fi构造的实际候选者-通常不是最佳选择,但可能在这里。:)(或者,也许情况下开关。)
通配符

@Wildcard我编辑了答案。现在,我们对鲤鱼和后tic的讨论没有任何意义。让我们删除此对话吗?
BrunoNegrãoZica

0

Plumbum是一种类似Python的外壳语言。它使用Python打包了类似shell的语法,从而使体验成为面向对象的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.