*nix용 객체 지향 셸

*nix용 객체 지향 셸

서문: 저는 bash를 좋아하며 어떤 종류의 논쟁이나 성전을 시작할 생각이 없습니다. 이것이 극도로 순진한 질문이 아니기를 바랍니다.

이 질문은 다소 관련이 있습니다.이 게시물슈퍼유저에 관해서는 OP가 그가 요구하는 것이 무엇인지 실제로 알지 못하는 것 같습니다. 저는 FreeBSD, Linux, OS X에서 bash를 사용하고 Windows에서는 cygwin을 사용합니다. 최근에는 Windows에서 PowerShell을 사용하는 경험도 많이 얻었습니다.

bash와 호환되지만 객체 지향 스크립팅 계층을 혼합에 추가하는 *nix용 셸(이미 사용 가능하거나 개발 중)이 있습니까? 내가 아는 유일한 것은 Python 콘솔이지만, 내가 아는 한 그것은 표준 쉘 환경에 대한 액세스를 제공하지 않습니다. 예를 들어 Python 콘솔에서는 cd ~and 를 사용할 수 없습니다 ls. chmod +x file이러한 작업을 수행하려면 표준 Unix 바이너리 대신 Python을 사용하거나 Python 코드를 사용하여 바이너리를 호출해야 합니다.

그런 껍질이 존재합니까?

답변1

쉘에는 세 가지 이상적인 기능이 있다고 생각됩니다.

  • 대화형 사용성: 자주 사용하는 명령을 빠르게 입력할 수 있어야 합니다.
  • 프로그래밍: 데이터 구조, 동시성(작업, 파이프라인...)
  • 시스템 액세스: 파일, 프로세스, 창, 데이터베이스, 시스템 구성 처리...

Unix 셸은 대화형 측면에 초점을 맞추고 대부분의 시스템 액세스와 일부 프로그래밍을 다음과 같은 외부 도구에 하청 계약하는 경향이 있습니다.

  • 기원전간단한 수학을 위해
  • OpenSSL암호화를 위해
  • sed,기타 텍스트 처리용
  • CNC기본 TCP/IP 네트워크와 함께 사용
  • ftpFTP의 경우
  • mail, Mailmailx기본 이메일의 경우
  • cron예약된 작업의 경우
  • 제어판기본 X 윈도우 작업의 경우
  • 데코프KDE ≤3.x 라이브러리의 경우
  • 버스도구( dbus-*또는버스) 다양한 시스템 정보 및 구성 작업(KDE ≥4와 같은 최신 데스크탑 환경 포함)

올바른 매개변수 또는 파이프 입력을 사용하여 명령을 호출하면 매우 많은 작업을 수행할 수 있습니다. 이것은 매우 강력한 접근 방식입니다. 모든 작업을 제대로 수행하는 도구를 갖는 것이 모든 작업을 제대로 수행하지 못하는 단일 프로그램보다 더 낫습니다. 그러나 한계가 있습니다.

내가 추구하는 "객체 지향 스크립팅" 요구 사항인 유닉스 셸의 주요 제한 사항 중 하나는 한 명령에서 다음 명령으로 정보를 유지하는 데 능숙하지 않거나 어떤 방식으로든 파이프보다 빠르다는 것입니다. 특히 프로그램 간 통신은 텍스트 기반이므로 호환 가능한 방식으로 데이터를 직렬화하는 경우에만 애플리케이션을 결합할 수 있습니다. 이는 축복이자 저주입니다. 모든 텍스트 접근 방식을 사용하면 간단한 작업을 신속하게 완료할 수 있지만 더 복잡한 작업에는 장벽이 추가됩니다.

대화형 사용성은 또한 프로그램 유지 관리성에 반합니다. 대화형 프로그램은 짧아야 하고, 참조가 거의 필요하지 않으며, 변수 선언이나 입력 등으로 사용자를 방해하지 않아야 합니다. 유지 관리 가능한 프로그램은 읽을 수 있어야 하고(약어가 많지 않아야 함), 읽을 수 있어야 하며(따라서 단어가 문자열, 함수 이름, 변수 이름 등인지 궁금해할 필요가 없음), 일관성이 있어야 합니다. 예를 들어 변수 선언 및 유형 등을 확인합니다.

대체로 쉘은 접근하기 어려운 타협점입니다. 좋아요, 이것으로 호언장담은 끝났습니다. 예를 들어 보겠습니다.


  • 이것펄 쉘(psh)"Unix 셸의 대화형 특성과 Perl의 강력한 기능을 결합합니다." 간단한 명령(파이프 포함)은 쉘 구문을 사용하여 입력할 수 있습니다. 그 외 모든 것은 Perl입니다. 이 프로젝트는 오랫동안 개발되어 왔습니다. 사용할 수 있지만 순수 Perl(스크립팅용)이나 순수 셸(대화형 또는 스크립팅용) 대신 사용을 고려할 정도는 아닙니다.

  • 파이썬특히 수치 및 병렬 컴퓨팅을 위한 개선된 대화형 Python 콘솔입니다. 이것은 비교적 젊은 프로젝트입니다.

  • irb(대화형 루비)Python 콘솔에 대응하는 Ruby입니다.

  • SSH전통적으로 UNIX 쉘에서 발견되는 시스템 바인딩 유형(문자열, 프로세스, 파일)을 사용하는 체계 구현(즉, 훌륭한 프로그래밍 언어)입니다. 그러나 대화형 셸로 사용하기 위한 것은 아닙니다.

  • 다루기 힘든향상된 대화형 쉘입니다. 그 강점은 상호작용성(명령줄 편집, 완성, 간결하지만 난해한 구문을 사용하여 수행되는 일반적인 작업)입니다. 프로그래밍 기능은 ksh와 마찬가지로 훌륭하지는 않지만 터미널 제어, 정규식, 네트워킹 등을 위한 많은 라이브러리가 함께 제공됩니다.

  • 물고기Unix 스타일 쉘의 깔끔한 시작입니다. 더 나은 프로그래밍이나 시스템 액세스 기능이 없습니다. sh와의 호환성이 깨졌기 때문에 더 나은 기능을 발전시킬 여지가 더 많았지만 그런 일은 일어나지 않았습니다.


부록: 유닉스 툴킷의 또 다른 부분은 많은 것을 파일로 취급합니다:

  • 대부분의 하드웨어 장치는 파일로 액세스할 수 있습니다.
  • Linux에서는 /sys더 많은 하드웨어 및 시스템 제어가 제공됩니다.
  • 많은 UNIX 변형에서는 파일 시스템을 통해 프로세스 제어를 수행할 수 있습니다 /proc.
  • 퓨즈새로운 파일 시스템을 쉽게 작성할 수 있습니다. 동적으로 파일 형식을 변환하고, 다양한 네트워크 프로토콜을 통해 파일에 액세스하고, 아카이브 내부를 보는 등의 기능을 제공하는 기존 파일 시스템이 이미 존재합니다.

아마도 유닉스 셸의 미래는 명령을 통한 더 나은 시스템 액세스(및 명령을 결합하는 더 나은 제어 구조)가 아니라 파일 시스템을 통한 더 나은 시스템 액세스(그 조합은 약간 다릅니다. 우리가 아직 파악하지 못한 것 같습니다) 아직 핵심 관용구는 무엇입니까(예: 아직 쉘 앤 튜브가 아님).

답변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

클래스는 배열을 사용하여 구현되므로수업그리고설치하다실제로 동의어 - 자바스크립트의 동의어와 비슷합니다.

이 대기열에 항목을 추가하는 방법은 다음과 같습니다.

$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

가지다서두르다루비를 사용하며푸시이것은 펄 기반입니다.

관련 정보