* nix用のオブジェクト指向シェル

* nix用のオブジェクト指向シェル

はじめに:私はbashが好きで、何らかの議論や神殿を始めるつもりはありません。これが非常に素朴な質問ではないことを願っています。

この質問は多少関連しています。この投稿スーパーユーザーに関しては、OPが彼が要求するものが何であるかを実際に知らないようです。私はFreeBSD、Linux、OS Xでbashを使用し、Windowsではcygwinを使用します。最近では、WindowsでPowerShellを使用した経験もたくさんありました。

bashと互換性がありますが、オブジェクト指向のスクリプト層を混合に追加する* nix用のシェル(すでに利用可能または開発中)はありますか?私が知っている唯一のものはPythonコンソールですが、私が知っている限り、それは標準のシェル環境へのアクセスを提供しません。たとえば、Pythonコンソールではcd ~andを使用できませんlschmod +x fileこれを行うには、標準のUnixバイナリの代わりにPythonを使用するか、Pythonコードを使用してバイナリを呼び出す必要があります。

そんなシェルが存在しますか?

答え1

シェルには3つの理想的な機能があると考えられています。

  • 対話型ユーザビリティ:頻繁に使用するコマンドをすばやく入力できる必要があります。
  • プログラミング:データ構造、並行性(作業、パイプライン...)
  • システムアクセス:ファイル、プロセス、ウィンドウ、データベース、システム構成の処理...

Unixシェルはインタラクティブな側面に焦点を当て、ほとんどのシステムアクセスといくつかのプログラミングを以下の外部ツールに下請契約する傾向があります。

  • 紀元前簡単な数学のために
  • OpenSSL暗号化のため
  • sedアッその他のテキスト処理用
  • CNCデフォルトのTCP / IPネットワークで使用
  • ftpFTPの場合
  • mailMailなどmailx基本メールの場合
  • cronスケジュールされたジョブの場合
  • コントロールパネル基本的なXウィンドウ操作の場合
  • デコフKDE ≤3.x ライブラリの場合
  • バスツール(dbus-*またはバス)さまざまなシステム情報と構成作業(KDE ≥4などの最新のデスクトップ環境を含む)

正しいパラメータまたはパイプ入力を使用してコマンドを呼び出すと、非常に多くのことができます。これは非常に強力なアプローチです。すべてのタスクを正しく実行するツールを持つことは、すべてのタスクを正しく実行できない単一のプログラムよりも優れています。しかし、限界があります。

私が求めている「オブジェクト指向スクリプト」の要件であるUnixシェルの主な制限の1つは、1つのコマンドから次のコマンドに情報を保持するのに慣れていないか、パイプでより速いということです。特に、プログラム間通信はテキストベースであるため、互換性のある方法でデータをシリアル化する場合にのみアプリケーションを組み合わせることができます。これは祝福と呪いです。すべてのテキストアプローチを使用すると、簡単な作業をすばやく完了できますが、より複雑な作業には障壁が追加されます。

インタラクティブなユーザビリティもプログラムの保守性に反します。インタラクティブプログラムは短くなければならず、参照はほとんど必要ではなく、変数宣言や入力などでユーザーを邪魔しないでください。メンテナンス可能なプログラムは読み取り可能でなければならず(頭字語が多いことはありません)、読み取り可能でなければならず(したがって、単語が文字列、関数名、変数名などであるかどうか疑問に思う必要はありません)、一貫している必要があります。たとえば、変数宣言や型などを確認します。

通常、シェルはアクセスしにくい妥協点です。わかりました、これで豪言長談は終わりました。たとえば、見てみましょう。


  • これパールシェル(psh)「Unixシェルの対話型特性とPerlの強力な機能を組み合わせます。」単純なコマンド(パイプを含む)はシェル構文を使用して入力できます。それ以外はすべてPerlです。このプロジェクトは長い間開発されてきました。使用できますが、純粋なPerl(スクリプト用)または純粋なシェル(対話型またはスクリプト用)の代わりに使用を検討するのではありません。

  • Python特に、数値および並列コンピューティングのための改良された対話型Pythonコンソールです。これは比較的若いプロジェクトです。

  • irb(対話型ルビー)Pythonコンソールに対応するRubyです。

  • SSH伝統的に、UNIXシェルで見つかったシステムバインディングタイプ(文字列、プロセス、ファイル)を使用するスキームの実装(つまり、優れたプログラミング言語)です。ただし、対話型シェルとして使用するためのものではありません。

  • 扱いにくい強化された対話型シェルです。その強みは、相互作用性(コマンドラインの編集、完成、簡潔で難しい構文を使用して実行される一般的な作業)です。プログラミング機能はkshと同様に優れていませんが、端末制御、正規表現、ネットワーキングなどのための多くのライブラリが付属しています。

  • Unixスタイルのシェルのすっきりとしたスタートです。より良いプログラミングやシステムアクセス機能はありません。 shとの互換性が壊れているので、より良い機能を発展させる余地がたくさんありましたが、そのようなことは起こりませんでした。


付録: Unix ツールキットの別の部分は多くをファイルとして扱います:

  • ほとんどのハードウェアデバイスはファイルとしてアクセスできます。
  • Linux では、/sysより多くのハードウェアおよびシステム制御が提供されます。
  • 多くのUNIXバリアントでは、ファイルシステムを介してプロセス制御を実行できます/proc
  • ヒューズ新しいファイルシステムを簡単に作成できます。動的にファイル形式を変換し、さまざまなネットワークプロトコルを介してファイルにアクセスし、アーカイブの内部を表示するなどの機能を提供する既存のファイルシステムがすでに存在しています。

おそらくUnixシェルの将来は、コマンドを介したより良いシステムアクセス(およびコマンドを組み合わせたより良い制御構造)ではなく、ファイルシステムを介したより良いシステムアクセス(その組み合わせはわずかに異なります。まだ重要なイディオムは何ですか(例:まだシェルアンドチューブではありません)。

答え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 シェル構文を維持しながら、いくつかのオブジェクト指向の概念を導入します。http://blog.fpmurphy.com/2010/05/ksh93-using-types-to-create-object- Oriented-scripts.html

答え4

持つ急ぐルビーを使ってプッシュこれはパールベースです。

関連情報