เชลล์เชิงอ็อบเจ็กต์สำหรับ * ระวัง


38

คำนำ: ฉันรักการทุบตีและไม่มีความตั้งใจที่จะเริ่มโต้เถียงหรือสงครามศักดิ์สิทธิ์และหวังว่านี่จะไม่ใช่คำถามที่ไร้เดียงสาอย่างยิ่ง

คำถามนี้ค่อนข้างเกี่ยวข้องกับโพสต์นี้ใน superuser แต่ฉันไม่คิดว่า OP รู้จริง ๆ ว่าเขาขออะไร ฉันใช้ bash บน FreeBSD, linux, OS X และ cygwin บน Windows ฉันเคยมีประสบการณ์มากมายกับ PowerShell บน Windows เมื่อเร็ว ๆ นี้

มีเชลล์สำหรับ * nix, มีอยู่แล้วหรือในงานที่เข้ากันได้กับ bash แต่เพิ่มเลเยอร์ของสคริปต์เชิงวัตถุลงในมิกซ์หรือไม่ สิ่งเดียวที่ฉันรู้ว่าใกล้เข้ามาคือคอนโซลหลาม แต่เท่าที่ฉันสามารถบอกได้ว่ามันไม่ได้ให้การเข้าถึงสภาพแวดล้อมเชลล์มาตรฐาน ตัวอย่างเช่นผมไม่สามารถเพียงcd ~และlsจากนั้นchmod +x fileภายในคอนโซลหลาม ฉันจะต้องใช้หลามเพื่อทำงานเหล่านั้นมากกว่าไบนารียูนิกซ์มาตรฐานหรือโทรไบนารีโดยใช้รหัสหลาม

มีเชลล์อยู่จริงหรือไม่?


3
มีPashแต่นั่นเป็น Powershell-like มากกว่า Bash-like
ephemient

1
@ephemient บางทีคุณควรเขียนคำตอบสำหรับ pash ... แม้ว่าฉันจะไม่รู้ว่ามันคืออะไร iirc, powershell เป็นเชลล์ OO
xenoterracide

4
Hey, คุณควรตรวจสอบipython หากคุณป้อนนิพจน์ที่ไม่สมเหตุสมผลเหมือนงูหลามมันจะพยายามจับคู่กับคำสั่งเชลล์ ตัวอย่างเช่นสิ่งที่cd ~ตามมาด้วยlsงานเหมือนใน Bash นอกจากนี้คุณยังสามารถส่งออกกำหนดตัวแปรหลาม (รายการของสาย.. ประเภท) listing = !lsที่มีคำสั่งเช่น
สัญชาตญาณ

@intuited: ยอดเยี่ยมฉันจะตรวจสอบมันออกมา
Robert S Ciaccio

1
@intuited: iPython ค่อนข้างดีสำหรับสิ่งที่ฉันต้องการจะทำขอบคุณ!
Robert S Ciaccio

คำตอบ:


43

ฉันสามารถคิดถึงคุณสมบัติสามอย่างที่ต้องการในเชลล์:

  • การใช้งานแบบโต้ตอบ: คำสั่งทั่วไปควรจะพิมพ์ได้อย่างรวดเร็ว เสร็จสิ้น; ...
  • การเขียนโปรแกรม: โครงสร้างข้อมูล การทำงานพร้อมกัน (งาน, ไปป์, ... ); ...
  • การเข้าถึงระบบ: การทำงานกับไฟล์กระบวนการ windows ฐานข้อมูลการกำหนดค่าระบบ ...

Unix shells มีแนวโน้มที่จะให้ความสำคัญกับการโต้ตอบและการรับช่วงการเข้าถึงระบบส่วนใหญ่และบางส่วนของการเขียนโปรแกรมไปยังเครื่องมือภายนอกเช่น:

  • bcสำหรับคณิตศาสตร์อย่างง่าย
  • opensslสำหรับการเข้ารหัส
  • sed , awkและอื่น ๆ สำหรับการประมวลผลข้อความ
  • ncสำหรับเครือข่าย TCP / IP พื้นฐาน
  • ftp สำหรับ FTP
  • mail, Mail, mailxฯลฯ สำหรับ e-mail พื้นฐาน
  • cron สำหรับงานที่กำหนด
  • wmctrlสำหรับการจัดการหน้าต่าง X พื้นฐาน
  • dcopสำหรับไลบรารี KDE ≤3.x
  • เครื่องมือdbus ( dbus-*หรือqdbus ) สำหรับข้อมูลระบบและงานการตั้งค่าต่างๆ (รวมถึงสภาพแวดล้อมเดสก์ทอปที่ทันสมัยเช่น KDE modern4)

มีหลายสิ่งหลายอย่างที่สามารถทำได้โดยการเรียกใช้คำสั่งด้วยอาร์กิวเมนต์ที่ถูกต้องหรือไพพ์อินพุต นี่เป็นวิธีที่ทรงพลังมาก - มีหนึ่งเครื่องมือต่องานที่ทำได้ดีกว่าโปรแกรมเดียวที่ทำทุกอย่าง แต่ไม่ดี - แต่มีข้อ จำกัด

ข้อ จำกัด ที่สำคัญของยูนิกซ์เชลล์และฉันสงสัยว่านี่คือสิ่งที่คุณตามมาด้วยความต้องการ“ การเขียนสคริปต์เชิงวัตถุ” ของคุณนั่นคือพวกเขาไม่เก่งในการเก็บข้อมูลจากคำสั่งหนึ่งไปยังอีกหรือการรวมคำสั่งต่าง ๆ ท่อ โดยเฉพาะอย่างยิ่งการสื่อสารระหว่างโปรแกรมเป็นแบบข้อความดังนั้นแอปพลิเคชันสามารถรวมกันได้หากพวกเขาเป็นอันดับข้อมูลในวิธีที่เข้ากันได้ นี่เป็นทั้งคำอวยพรและคำสาป: วิธีการทุกอย่างคือข้อความทำให้ง่ายต่อการทำงานอย่างรวดเร็ว แต่เพิ่มอุปสรรคสำหรับงานที่ซับซ้อนมากขึ้น

ความสามารถในการใช้งานอินเตอร์แอคทีฟนั้นค่อนข้างที่จะต่อต้านการบำรุงรักษาของโปรแกรม โปรแกรมแบบอินเทอร์แอคทีฟควรสั้นต้องใช้การอ้างอิงน้อยไม่รบกวนคุณด้วยการประกาศตัวแปรหรือการพิมพ์ ฯลฯ โปรแกรมที่รักษาได้ควรอ่านได้ (ดังนั้นจึงไม่มีตัวย่อหลายตัว) ควรอ่านได้ (ดังนั้นคุณไม่ต้องสงสัยว่า เป็นสตริงชื่อฟังก์ชันชื่อตัวแปร ฯลฯ ) ควรมีการตรวจสอบความสอดคล้องเช่นการประกาศตัวแปรและการพิมพ์เป็นต้น

โดยสรุปเปลือกเป็นประนีประนอมที่ยากต่อการเข้าถึง ตกลงนี่จะเป็นการสิ้นสุดส่วนของการคุยโวในตัวอย่าง


  • Perl เชลล์ (PSH) “รวมลักษณะการโต้ตอบของเชลล์ยูนิกซ์กับพลังของภาษา Perl” สามารถป้อนคำสั่งง่าย ๆ (แม้แต่ไพพ์ไลน์) ในไวยากรณ์เชลล์ ทุกอย่างอื่นคือ Perl โครงการไม่ได้มีการพัฒนามาเป็นเวลานาน มันใช้งานได้ แต่ยังไม่ถึงจุดที่ฉันจะพิจารณาใช้มันมากกว่า Perl บริสุทธิ์ (สำหรับการเขียนสคริปต์) หรือเชลล์บริสุทธิ์ (โต้ตอบหรือสคริปต์)

  • IPythonเป็นคอนโซล Python แบบโต้ตอบที่ได้รับการปรับปรุงโดยเฉพาะอย่างยิ่งการกำหนดเป้าหมายที่การคำนวณเชิงตัวเลขและแบบขนาน นี่เป็นโครงการที่ค่อนข้างใหม่

  • irb (ruby แบบอินเทอร์แอคทีฟ)คือ Ruby เทียบเท่ากับ Python console

  • scshคือการดำเนินการตามโครงการ (เช่นภาษาการเขียนโปรแกรมที่ดี) กับชนิดของการผูกระบบแบบดั้งเดิมที่พบในยูนิกซ์เชลล์ (สตริงกระบวนการกระบวนการไฟล์) มันไม่ได้มีจุดมุ่งหมายที่จะใช้งานได้ในฐานะเชลล์เชิงโต้ตอบ

  • zshเป็นเชลล์แบบโต้ตอบที่ได้รับการปรับปรุง จุดแข็งของมันคือการโต้ตอบ (รุ่นบรรทัดคำสั่งเสร็จสมบูรณ์งานทั่วไปสำเร็จด้วย terse แต่ไวยากรณ์ลับ) คุณสมบัติการเขียนโปรแกรมมันไม่ได้ยอดเยี่ยม (เทียบกับ ksh) แต่มันมาพร้อมกับไลบรารีจำนวนมากสำหรับการควบคุมเทอร์มินัล regexps เครือข่าย ฯลฯ

  • ปลาเป็นจุดเริ่มต้นที่สะอาดที่เปลือกหอยยูนิกซ์ มันไม่มีคุณสมบัติการเขียนโปรแกรมหรือระบบที่ดีกว่า เนื่องจากมันเข้ากันได้กับ sh จึงมีพื้นที่มากขึ้นในการพัฒนาฟีเจอร์ที่ดีกว่า แต่นั่นไม่ได้เกิดขึ้น


ภาคผนวก: อีกส่วนหนึ่งของกล่องเครื่องมือยูนิกซ์กำลังทำสิ่งต่าง ๆ เป็นไฟล์:

  • อุปกรณ์ฮาร์ดแวร์ส่วนใหญ่สามารถเข้าถึงได้เป็นไฟล์
  • ภายใต้ Linux /sysให้การควบคุมฮาร์ดแวร์และระบบเพิ่มเติม
  • ในระบบปฏิบัติการยูนิกซ์หลายรุ่นการควบคุมกระบวนการสามารถทำได้ผ่าน/procระบบไฟล์
  • FUSEทำให้ง่ายต่อการเขียนระบบไฟล์ใหม่ มีระบบไฟล์ที่มีอยู่แล้วสำหรับการแปลงรูปแบบไฟล์ได้ทันทีเข้าถึงไฟล์ผ่านโปรโตคอลเครือข่ายที่หลากหลายมองเข้าไปในที่เก็บถาวรเป็นต้น

บางทีอนาคตของยูนิกซ์เชลล์อาจไม่ใช่การเข้าถึงระบบที่ดีกว่าผ่านคำสั่ง (และโครงสร้างการควบคุมที่ดีกว่าในการรวมคำสั่ง) แต่การเข้าถึงระบบที่ดีขึ้นผ่านระบบไฟล์ (ซึ่งรวมกันค่อนข้างแตกต่างกัน - ฉันไม่คิดว่าเรา ท่อเปลือก) ยัง)


1
คุณกดตะปูที่หัวหลังจากคุณพูดว่า "ฉันสงสัยว่านี่คือสิ่งที่คุณตามหา" เหตุผลหลักที่ฉันถามคำถามนี้คือฉันชอบที่จะมีพลังของเครื่องมือยูนิกซ์ แต่การโต้ตอบด้วยข้อความระหว่างโปรแกรมต่าง ๆ ทำให้เกิดอุปสรรคสำหรับงานที่ซับซ้อนมากขึ้น ฉันใช้เวลาในการเขียนโปรแกรมเขียนข้อความมากพอแล้วฉันคิดว่านี่เป็นคำตอบที่ดีมาก มันเป็นหัวใจของปัญหาและความซับซ้อนของหัวเรื่อง ฉันหวังว่าฉันจะโหวตได้สองครั้ง: P
Robert S Ciaccio

1
+1 สำหรับ ipython แม้ว่าฉันไม่รู้ว่า OP ต้องการทำอะไร
Falmarri

1
นี่คือคำตอบที่ดี: ฉันคิดอย่างสุจริตว่าวิทยานิพนธ์ของปริญญาเอกที่น่าสนใจอยู่ที่นี่แล้ว
Ziggy

1
@RobertSCiaccio คำตอบนี้เพิ่งเชื่อมโยงกับโพสต์เมื่อเร็ว ๆ นี้และความคิดเห็นของคุณเกี่ยวกับการแยกวิเคราะห์ข้อความทำให้ฉันคิดว่า ... หากการแยกวิเคราะห์ข้อความเพียงพอที่จะทำ "งานที่ซับซ้อน" ให้สำเร็จคุณก็ไม่มีสคริปต์หรือโปรแกรมขนาดเล็ก มันและใช้เป็นฟังก์ชั่นบางอย่างในสคริปต์ทุบตีของคุณ? แค่ความคิดฉันไม่ได้มีประสบการณ์การเขียนสคริปต์ทุบตีมากเกินไปภายใต้เข็มขัดของฉันที่จะพูดถึง
Oxwivi

1
@onlyanegg ในทางใดบ้างที่ปลาจะบอกว่าเป็น "เชิงวัตถุ"? ปลาส่วนใหญ่มีจุดมุ่งหมายที่เรียบง่าย มีวิธีใดบ้างที่มีประสิทธิภาพมากกว่าทางเลือกอื่น ๆ
Gilles 'หยุดความชั่วร้าย' ใน

13

คุณไม่จำเป็นต้องใช้รหัสทุบตีมากในการนำคลาสหรือวัตถุมาใช้ในการทุบตี

บอกว่า 100 บรรทัด

Bash มีอาเรย์แบบเชื่อมโยงที่สามารถใช้ในการสร้างระบบอ็อบเจกต์อย่างง่ายพร้อมการสืบทอดวิธีการและคุณสมบัติ

ดังนั้นคุณอาจกำหนดคลาสดังนี้:

class Queue N=10 add=q_add remove=q_remove

การสร้างอินสแตนซ์ของคิวนี้อาจทำได้ดังนี้:

class Q:Queue N=100

หรือ

inst Q:Queue N=100

เนื่องจากคลาสมีการใช้งานกับอาเรย์คลาสและinstจึงเป็นคำพ้องความหมายจริงๆ - เรียงลำดับเหมือนใน 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
}

และเพื่อทิ้งโครงสร้างวัตถุฉันทำสิ่งนี้:

หมายเหตุ: สิ่งนี้ไม่จำเป็นสำหรับ 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 ของฉันไม่ได้พิจารณาวัตถุภายในวัตถุ - ยกเว้นคลาสที่สืบทอดมา คุณสามารถสร้างมันแยกกันหรือสร้างเป็นพิเศษเช่นคลาส () * obj_dump * จะต้องได้รับการแก้ไขเพื่อตรวจสอบคลาสภายในเพื่อพิมพ์ซ้ำ

Oh! และฉันกำหนดคลาส 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

สร้างอินสแตนซ์คิวบางส่วนและทำให้ทำงาน:

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มีไว้สำหรับ ทำให้ผมนึกถึงคำตอบเกี่ยวกับสเตฟานว่าทำไมไม่ใช้ลูปเชลล์ข้อความกระบวนการโดยเฉพาะอย่างยิ่งในส่วนหัว "แนวคิด" ที่มีรายละเอียดแตกต่างในวัตถุประสงค์ระหว่างภาษาเช่น C bashและ unix.stackexchange.com/a/169765/135943
Wildcard

1
อาจจะทำงานอย่างไร มันใช้งานได้ แต่ประเด็นของคุณคือถึงแม้ว่าจะไม่ผิดก็แค่ไม่ได้ทำ ฉันยังไม่ได้ตอบคำถาม OP แต่ถ้าทุบตีเป็น TC แล้วฉันคิดว่ามันควรจะสามารถประมวลผลวัตถุ และหลายคนได้แสดงให้เห็นถึงสิ่งนี้
philcolbourn


5

IPythonสะดวกในการใช้อย่างน่าประหลาดใจ

คุณสมบัติเชลล์มาตรฐาน: การควบคุมงานการแก้ไขและประวัติ readline นามแฝงcat ls cdและpwdการรวมเพจเจอร์การรันคำสั่งระบบใด ๆ โดยนำหน้าด้วย!หรือเปิดใช้งาน%rehashxเอาต์พุตคำสั่งที่กำหนดให้กับตัวแปร python ค่า python ที่พร้อมใช้งานเป็นตัวแปรเชลล์

เฉพาะ Python: การนำผลลัพธ์จากคำสั่งล่าสุดกลับมาใช้อย่างรวดเร็วเข้าถึงเอกสารและแหล่งที่มาการโหลดโมดูลการดีบัก บางคลัสเตอร์สนับสนุนหากคุณเป็นเช่นนั้น

ที่กล่าวว่าการใช้งานท่อที่ซับซ้อนไม่ได้ทำใน Python คุณจะใช้เปลือกหอย posix เช่นกันด้วยกาวเพื่อส่งผ่านค่าไปมา


2

มีกำลังเร่งด่วนซึ่งใช้ ruby ​​และPshซึ่งยึดตาม perl


เว็บไซต์สำหรับ Rush ไม่มีการเผยแพร่อีกต่อไป ต่อไปนี้เป็นรุ่นที่เก็บไว้และนี่คือrepo GitHub เห็นได้ชัดว่าโครงการได้รับการฟื้นฟูที่นี่ ( โพสต์บล็อก )
waldyrious


2

อันนี้ค่อนข้างง่ายต่อการใช้และตั้งค่ามีชื่อ args และอื่น ๆ https://github.com/uudruid74/bashTheObjects

ฉันกำลังอัปเดตคำตอบของฉันด้วยตัวอย่างซึ่งติดตามหนึ่งในตัวอย่างพื้นฐานที่ให้ไว้สำหรับคำตอบอื่น แต่ด้วยไวยากรณ์นี้ โปรแกรมตัวอย่างนั้นคล้ายกัน แต่คุณไม่จำเป็นต้องนำหน้าตัวแปรทั้งหมดด้วย classname (มันรู้วิธีการแสดงkindof ) และฉันคิดว่าไวยากรณ์นั้นง่ายกว่ามาก !

ก่อนอื่นไฟล์คลาส ค่าเริ่มต้นสำหรับตัวแปรอินสแตนซ์เป็นทางเลือกและจะใช้เฉพาะเมื่อคุณไม่ส่งค่าเหล่านี้ไปยังตัวสร้าง

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. การยืนยันครั้งสุดท้ายนั้นจะล้มเหลว ซึ่งแตกต่างจากตัวอย่างข้างต้นห้องสมุดยังไม่สนับสนุนการกำหนดวัตถุ แต่ก็ไม่ยากที่จะเพิ่ม ฉันจะวางมันลงในสิ่งที่ต้องทำพร้อมกับการสนับสนุน container / iterator ที่จะเกิดขึ้น

คำสั่งการนำเข้าไม่จำเป็นต้องใช้ทางเทคนิค แต่บังคับให้โหลดคลาส ณ จุดที่กำหนดแทนที่จะรอใหม่ครั้งแรกซึ่งสามารถช่วยเริ่มต้นสิ่งต่าง ๆ ตามลำดับที่เหมาะสม จดบันทึกความง่ายที่คุณสามารถตั้งค่าตัวแปรอินสแตนซ์หลายตัวพร้อมกันทั้งหมด

นอกจากนี้ยังมีระดับการดีบักคอนสตรัคเตอร์ destructors คลาสย่อยและระบบการสะท้อนพื้นฐานและแสดงเป็นพิมพ์ / 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" (และเพิ่ม NewLine) ไม่มีใครจะประทับใจกับผลลัพธ์นี้มากนัก แต่เราจะรู้ว่านี่ไม่ง่ายอย่างที่เห็น :)

การเข้ารหัสมีความสุข!


การทำสิ่งที่ซับซ้อนนั้นง่ายกว่าการทำสิ่งที่ซับซ้อนให้ง่ายกว่า
สัญลักษณ์แทน

1

นี่เป็นเชลล์เชิงวัตถุที่อยู่ใน Python แต่มี sintaxe ปิดของ Golang: https://github.com/alexst07/shell-plus-plus

ตัวอย่างเช่นลองจับ:

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)

และคุณสามารถใช้คำสั่งทุบตีที่คล้ายกัน:

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.stop p1.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


}

เพียงแค่พยายามแนะนำแนวคิด oo เพื่อทุบตีอ้างอิงhttp://hipersayanx.blogspot.in/2012/12/object-oriented-programming-in-bash.htmlอ้างอิง

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 ได้สร้างบทความเขียนโปรแกรมเชิงวัตถุในทุบตี โดยทั่วไปเขา Hi-jacked $FUNCNAME, function, compgenและexportการสร้างใกล้เคียงกับ OOP หนึ่งจะได้รับในการทุบตี

ส่วนที่เจ๋งคือมันทำงานได้ดีและมีเพียงหนึ่งบรรทัดในการสร้างคลาส

ชิ้นส่วนพื้นฐานที่จำเป็นคือ:

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 มีรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการใช้งานได้จริงบนเว็บไซต์ของเขา คำเตือนเรื่องค่าโดยสารแม้ว่านี่จะเป็นการใช้งานไม่ได้กับสิ่งที่เก่ากว่า bash 4.0 และอาจทำให้ปวดหัวในการดีบั๊ก ในขณะที่โดยส่วนตัวฉันต้องการเห็นส่วนใหญ่ของการชุบหม้อไอน้ำทำซ้ำเป็นชั้นเรียนของตัวเอง

มันฉลาดกว่าที่จะใช้ภาษาสคริปต์ OOP อย่างจริงจังเช่น perl, ruby ​​และ python เมื่อเหมาะสมกับโครงการของคุณมากขึ้น อย่างไรก็ตามในตัวเลือกที่ซื่อสัตย์ของฉันมันคุ้มค่าเวลาและความพยายามเมื่อรักษาสคริปต์ทุบตีแบบแยกส่วนเพื่อใช้วิธีการของ OOP นี้ในทุบตี


0

ฉันกำลังพัฒนาบน GitHub ฟังก์ชั่นที่ใช้งานได้เช่นเดียวกับที่HashMap วัตถุ , shell_map

เพื่อสร้าง " อินสแตนซ์ HashMap " ฟังก์ชั่นนี้สามารถสร้างสำเนาของตัวเองภายใต้ชื่อที่แตกต่างกัน การคัดลอกฟังก์ชั่นใหม่แต่ละอันจะมีตัวแปร $ FUNCNAME ที่แตกต่างกัน $ FUNCNAME จะถูกใช้เพื่อสร้างเนมสเปซสำหรับแต่ละอินสแตนซ์แผนที่

ปุ่มแผนที่เป็นตัวแปรทั่วโลกในรูปแบบ $ 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!"

คุณดูเหมือนจะเข้าใจผิดว่าการทดแทนคำสั่งทำงานอย่างไร มันจะถูกแทนที่ด้วยการส่งออกของคำสั่งที่มีอยู่ภายใน backticks ไม่แทนที่ด้วยสถานะกลับ กล่าวอีกนัยหนึ่งรหัสของคุณไม่ได้ทำในสิ่งที่คุณคิด
สัญลักษณ์แทน

เอเอชเอ คุณถูก. ขอโทษด้วย. อย่างไรก็ตามมันจะมีความชัดเจนมากขึ้นในการใช้carp "Some error message"; returnแทน
สัญลักษณ์ตัวแทน

หรือ[ -z "$KEY" ] && { carp "some message"; return;} ไม่ต้องการ subshell แต่ที่จริงแล้วดูเหมือนว่าผู้สมัครจริงสำหรับการif ... elif ... elif ... else ... fiสร้าง - ซึ่งไม่ใช่ทางเลือกที่ดีที่สุด แต่อาจเป็นที่นี่ :) (หรืออาจเป็นการสลับกรณี)
Wildcard

@ Wildcard ฉันแก้ไขคำตอบแล้ว ตอนนี้การสนทนาของเราเกี่ยวกับปลาคาร์พและ backticks ไม่มีเหตุผล มาลบบทสนทนานี้กันไหม?
Bruno Negrão Zica

0

Plumbumเป็นภาษาเชลล์แบบ Python มันบรรจุเชลล์เช่นเดียวกับไวยากรณ์กับ Python ทำให้ object-orientated เน้นประสบการณ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.