ฟังก์ชั่นตกแต่ง Bash


10

ในไพ ธ อนเราสามารถตกแต่งฟังก์ชั่นด้วยรหัสที่ใช้โดยอัตโนมัติและดำเนินการกับฟังก์ชั่น

มีคุณสมบัติคล้ายกันในทุบตี?

ในสคริปต์ที่ฉันกำลังทำงานอยู่ฉันมีสำเร็จรูปบางส่วนที่ทดสอบข้อโต้แย้งที่จำเป็นและออกหากไม่มีอยู่ - และแสดงข้อความบางอย่างหากระบุการตั้งค่าการดีบัก

น่าเสียดายที่ฉันต้องใส่รหัสนี้ในทุก ๆ ฟังก์ชั่นและถ้าฉันต้องการที่จะเปลี่ยนมันฉันจะต้องแก้ไขทุกฟังก์ชั่น

มีวิธีลบรหัสนี้จากแต่ละฟังก์ชั่นและนำไปใช้กับฟังก์ชั่นทั้งหมดซึ่งคล้ายกับมัณฑนากรในหลามหรือไม่?


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

คำตอบ:


12

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

decorate() {
  eval "
    _inner_$(typeset -f "$1")
    $1"'() {
      echo >&2 "Calling function '"$1"' with $# arguments"
      _inner_'"$1"' "$@"
      local ret=$?
      echo >&2 "Function '"$1"' returned with exit status $ret"
      return "$ret"
    }'
}

f() {
  echo test
  return 12
}
decorate f
f a b

ซึ่งจะส่งออก:

Calling function f with 2 arguments
test
Function f returned with exit status 12

คุณไม่สามารถเรียกการตกแต่งสองครั้งเพื่อตกแต่งฟังก์ชันของคุณสองครั้ง

ด้วยzsh:

decorate()
  functions[$1]='
    echo >&2 "Calling function '$1' with $# arguments"
    () { '$functions[$1]'; } "$@"
    local ret=$?
    echo >&2 "function '$1' returned with status $ret"
    return $ret'

Stephane - typesetจำเป็นหรือไม่ มันจะไม่ประกาศเป็นอย่างอื่นไหม?
mikeserv

@mikeserv eval "_inner_$(typeset -f x)"สร้าง_inner_xเป็นสำเนาที่แน่นอนของต้นฉบับx(เหมือนfunctions[_inner_x]=$functions[x]ในzsh)
Stéphane Chazelas

ฉันเข้าใจ - แต่ทำไมคุณถึงต้องการสองสิ่ง
mikeserv

คุณจำเป็นต้องมีบริบทที่แตกต่างมิฉะนั้นคุณจะไม่สามารถที่จะจับภายในreturn 's
Stéphane Chazelas

1
ฉันไม่ได้ติดตามคุณไปที่นั่น คำตอบของฉันคือความพยายามในฐานะแผนที่ใกล้เคียงของสิ่งที่ฉันเข้าใจว่าตกแต่งด้วยงูใหญ่
Stéphane Chazelas

5

ฉันได้พูดคุยเกี่ยวกับวิธีการด้านล่างวิธีการทำงานหลายต่อหลายครั้งก่อนหน้านี้ดังนั้นฉันจะไม่ทำมันอีกครั้ง ส่วนตัวรายการโปรดของตัวเองในหัวข้อที่มีที่นี่และที่นี่

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

ประกาศ

คุณแค่ต้องการฟังก์ชั่นที่ประกาศฟังก์ชั่นอื่น ๆ

_fn_init() { . /dev/fd/4 ; } 4<<INIT
    ${1}() { $(shift ; printf %s\\n "$@")
     } 4<<-REQ 5<<-\\RESET
            : \${_if_unset?shell will ERR and print this to stderr}
            : \${common_param="REQ/RESET added to all funcs"}
        REQ
            _fn_init $(printf "'%s' " "$@")
        RESET
INIT

เรียกใช้มัน

นี่ผมโทรเมื่อจะประกาศให้ฉันฟังก์ชั่นที่เรียกว่า_fn_initfn

set -vx
_fn_init fn \
    'echo "this would be command 1"' \
    'echo "$common_param"'

#OUTPUT#
+ _fn_init fn 'echo "this would be command 1"' 'echo "$common_param"'
shift ; printf %s\\n "$@"
++ shift
++ printf '%s\n' 'echo "this would be command 1"' 'echo "$common_param"'
printf "'%s' " "$@"
++ printf ''\''%s'\'' ' fn 'echo "this would be command 1"' 'echo "$common_param"'
#ALL OF THE ABOVE OCCURS BEFORE _fn_init RUNS#
#FIRST AND ONLY COMMAND ACTUALLY IN FUNCTION BODY BELOW#
+ . /dev/fd/4

    #fn AFTER _fn_init .dot SOURCES IT#
    fn() { echo "this would be command 1"
        echo "$common_param"
    } 4<<-REQ 5<<-\RESET
            : ${_if_unset?shell will ERR and print this to stderr}
            : ${common_param="REQ/RESET added to all funcs"}
        REQ
            _fn_init 'fn' \
               'echo "this would be command 1"' \
               'echo "$common_param"'
        RESET

ที่จำเป็น

ถ้าฉันต้องการเรียกใช้ฟังก์ชันนี้มันจะตายถ้าไม่_if_unsetได้ตั้งค่าตัวแปรสภาพแวดล้อม

fn

#OUTPUT#
+ fn
/dev/fd/4: line 1: _if_unset: shell will ERR and print this to stderr

โปรดทราบคำสั่งของร่องรอยเปลือก - ไม่เพียง แต่fnล้มเหลวเมื่อเรียกเมื่อ_if_unsetไม่มีการตั้งค่า แต่มันไม่เคยทำงานในสถานที่แรก นี่คือปัจจัยที่สำคัญที่สุดที่จะเข้าใจเมื่อทำงานกับการขยายเอกสารที่นี่ - พวกเขาจะต้องเกิดขึ้นก่อนเสมอเพราะพวกเขาเป็น<<inputสิ่งสุดท้าย

ข้อผิดพลาดมาจาก/dev/fd/4เนื่องจากเชลล์พาเรนต์ประเมินอินพุตนั้นก่อนส่งมอบให้กับฟังก์ชัน มันเป็นวิธีที่ง่ายที่สุดและมีประสิทธิภาพมากที่สุดในการทดสอบสภาพแวดล้อมที่จำเป็น

อย่างไรก็ตามความล้มเหลวสามารถแก้ไขได้ง่าย

_if_unset=set fn

#OUTPUT#
+ _if_unset=set
+ fn
+ echo 'this would be command 1'
this would be command 1
+ echo 'REQ/RESET added to all funcs'
REQ/RESET added to all funcs

มีความยืดหยุ่น

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

set +vx
_fn_init 'fn' \
               'echo "Hi! I am the first function."' \
               'echo "$common_param"'
_fn_init 'fn2' \
               'echo "This is another function."' \
               'echo "$common_param"'
_if_unset=set ;

_if_unsetดังกล่าวข้างต้นผมประกาศสองฟังก์ชั่นและการตั้งค่า ตอนนี้ก่อนที่จะเรียกฟังก์ชั่นทั้งสองฉันจะยกเลิกการตั้งค่าcommon_paramเพื่อให้คุณสามารถเห็นพวกเขาจะตั้งค่าตัวเองเมื่อฉันเรียกพวกเขา

unset common_param ; echo
fn ; echo
fn2 ; echo

#OUTPUT#
Hi! I am the first function.
REQ/RESET added to all funcs

This is another function.
REQ/RESET added to all funcs

และตอนนี้จากขอบเขตของผู้โทร:

echo $common_param

#OUTPUT#
REQ/RESET added to all funcs

แต่ตอนนี้ฉันต้องการให้มันเป็นอย่างอื่น:

common_param="Our common parameter is now something else entirely."
fn ; echo 
fn2 ; echo

#OUTPUT#
Hi! I am the first function.
Our common parameter is now something else entirely.

This is another function.
Our common parameter is now something else entirely.

และถ้าฉัน_if_unsetไม่ล้าง?

unset _if_unset ; echo
echo "fn:"
fn ; echo
echo "fn2:"
fn2 ; echo

#OUTPUT#
fn:
dash: 1: _if_unset: shell will ERR and print this to stderr

fn2:
dash: 1: _if_unset: shell will ERR and print this to stderr

RESET

หากคุณต้องการรีเซ็ตสถานะของฟังก์ชั่นในเวลาใด ๆ ก็สามารถทำได้อย่างง่ายดาย คุณต้องทำเท่านั้น (จากภายในฟังก์ชั่น):

. /dev/fd/5

ฉันบันทึกข้อโต้แย้งที่ใช้ในการประกาศฟังก์ชั่นเริ่มต้นใน5<<\RESETfile-descriptor ดังนั้นการ.dotหาว่าในเชลล์เมื่อใดก็ตามจะทำซ้ำขั้นตอนที่ตั้งไว้ในตอนแรก มันง่ายมากจริง ๆ และพกพาได้อย่างสมบูรณ์ถ้าคุณยินดีที่จะมองข้ามความจริงที่ว่า POSIX ไม่ได้ระบุเส้นทางโหนดอุปกรณ์ตัวอธิบายไฟล์ (ซึ่งเป็นสิ่งจำเป็นสำหรับเชลล์.dot)

คุณสามารถขยายพฤติกรรมนี้ได้อย่างง่ายดายและกำหนดค่าสถานะต่างๆสำหรับฟังก์ชันของคุณ

มากกว่า?

วิธีนี้แทบจะไม่ทำให้พื้นผิวมีรอยขีดข่วน ฉันมักจะใช้เทคนิคเหล่านี้เพื่อฝังฟังก์ชั่นตัวช่วยเล็ก ๆ ที่ประกาศได้ตลอดเวลาลงในอินพุตของฟังก์ชั่นหลัก - ตัวอย่างเช่นสำหรับตำแหน่งเพิ่มเติม$@อาร์เรย์ที่ต้องการ อันที่จริง - อย่างที่ฉันเชื่อมันต้องเป็นอะไรที่ใกล้เคียงกับนี้มาก คุณสามารถเห็นได้ว่าโปรแกรมเหล่านั้นตั้งชื่อได้ง่ายมาก

ฉันยังชอบที่จะประกาศฟังก์ชั่นเครื่องกำเนิดไฟฟ้าที่รับประเภทที่ จำกัด ของพารามิเตอร์แล้วกำหนดเดียวที่ใช้งานหรือมิฉะนั้นขอบเขต จำกัด เตาฟังก์ชั่นตามสายของแลมบ์ดา - หรือฟังก์ชั่นในบรรทัด - ที่เพียงunset -f's ตัวเองเมื่อ ตลอด คุณสามารถส่งผ่านฟังก์ชันเชลล์ได้


เป็นประโยชน์จากการที่ซับซ้อนพิเศษกับอธิบายไฟล์มากกว่าการใช้คืออะไรeval?
Stéphane Chazelas

@StephaneChazelas ไม่มีความซับซ้อนเพิ่มจากมุมมองของฉัน อันที่จริงฉันเห็นอีกด้านหนึ่ง นอกจากนี้การอ้างนั้นง่ายกว่ามากและ.dotทำงานกับไฟล์และสตรีมเพื่อให้คุณไม่ต้องเจอปัญหารายการอาร์กิวเมนต์ชนิดเดียวกันที่คุณอาจทำ ถึงกระนั้นก็อาจเป็นเรื่องของการตั้งค่า แน่นอนฉันคิดว่ามันสะอาดกว่า - โดยเฉพาะอย่างยิ่งเมื่อคุณเข้าสู่การ eval eval - นั่นเป็นฝันร้ายจากที่ฉันนั่ง
mikeserv

@StephaneChazelas มีข้อดีอย่างหนึ่งคือ - และมันค่อนข้างดีอย่างหนึ่ง eval เริ่มต้นและ eval ตัวที่สองไม่จำเป็นต้องย้อนกลับด้วยวิธีนี้ เอกสารประกอบจะถูกประเมินจากอินพุต แต่คุณไม่จำเป็นต้องให้.dotแหล่งข้อมูลจนกว่าคุณจะดีและพร้อมหรือไม่ก็เคย สิ่งนี้จะช่วยให้คุณมีอิสระมากขึ้นในการทดสอบการประเมินผล และมันให้ความยืดหยุ่นของรัฐกับการป้อนข้อมูล - ซึ่งสามารถจัดการวิธีการอื่น ๆ - evalแต่มันไกลอันตรายน้อยจากมุมมองที่มีมากกว่า
mikeserv

2

ฉันคิดว่าวิธีหนึ่งในการพิมพ์ข้อมูลเกี่ยวกับฟังก์ชั่นเมื่อคุณ

ทดสอบอาร์กิวเมนต์ที่จำเป็นและออกหากไม่มีอยู่ - และแสดงข้อความบางอย่าง

คือการเปลี่ยน bash builtin returnและ / หรือexitในตอนต้นของทุกสคริปต์ (หรือในบางไฟล์ที่คุณมาทุกครั้งก่อนที่จะรันโปรแกรม) ดังนั้นคุณพิมพ์

   #!/bin/bash
   return () {
       if [ -z $1 ] ; then
           builtin return
       else
           if [ $1 -gt 0 ] ; then
                echo function ${FUNCNAME[1]} returns status $1 
                builtin return $1
           else
                builtin return 0
           fi
       fi
   }
   foo () {
       [ 1 != 2 ] && return 1
   }
   foo

หากคุณทำสิ่งนี้คุณจะได้รับ:

   function foo returns status 1

อาจมีการอัปเดตอย่างง่ายดายด้วยการแก้ไขจุดบกพร่องหากคุณต้องการเช่นนี้

   #!/bin/bash
   VERBOSE=1
   return () {
       if [ -z $1 ] ; then
           builtin return
       else
           if [ $1 -gt 0 ] ; then
               [ ! -z $VERBOSE ] && [ $VERBOSE -gt 0 ] && echo function ${FUNCNAME[1]} returns status $1  
               builtin return $1
           else
               builtin return 0
           fi
       fi
    }    

วิธีการนี้จะดำเนินการเมื่อตัวแปร VERBOSE ถูกตั้งค่า (อย่างน้อยนั่นก็เป็นวิธีที่ฉันใช้ verbose ในสคริปต์ของฉัน) แน่นอนมันไม่ได้แก้ปัญหาของฟังก์ชั่นการตกแต่ง แต่มันสามารถแสดงข้อความในกรณีที่ฟังก์ชั่นส่งคืนสถานะที่ไม่เป็นศูนย์

ในทำนองเดียวกันคุณสามารถกำหนดใหม่exitโดยแทนที่ทุกกรณีreturnถ้าคุณต้องการออกจากสคริปต์

แก้ไข: ฉันต้องการเพิ่มที่นี่วิธีที่ฉันใช้ในการตกแต่งฟังก์ชั่นในทุบตีถ้าฉันมีพวกเขามากและซ้อนกันเช่นกัน เมื่อฉันเขียนสคริปต์นี้:

#!/bin/bash 
outer () { _
    inner1 () { _
        print "inner 1 command"
    }   
    inner2 () { _
        double_inner2 () { _
            print "double_inner1 command"
        } 
        double_inner2
        print "inner 2 command"
    } 
    inner1
    inner2
    inner1
    print "just command in outer"
}
foo_with_args () { _ $@
    print "command in foo with args"
}
echo command in body of script
outer
foo_with_args

และสำหรับผลลัพธ์ฉันจะได้สิ่งนี้:

command in body of script
    outer: 
        inner1: 
            inner 1 command
        inner2: 
            double_inner2: 
                double_inner1 command
            inner 2 command
        inner1: 
            inner 1 command
        just command in outer
    foo_with_args: 1 2 3
        command in foo with args

มันจะมีประโยชน์กับคนที่มีฟังก์ชั่นและต้องการที่จะแก้ปัญหาพวกเขาเพื่อดูว่าข้อผิดพลาดของฟังก์ชั่นที่เกิดขึ้น มันขึ้นอยู่กับสามฟังก์ชั่นซึ่งสามารถอธิบายได้ด้านล่าง:

#!/bin/bash 
set_indentation_for_print_function () {
    default_number_of_indentation_spaces="4"
    #                            number_of_spaces_of_current_function is set to (max number of inner function - 3) * default_number_of_indentation_spaces 
    #                            -3 is because we dont consider main function in FUNCNAME array - which is if your run bash decoration from any script,
    #                            decoration_function "_" itself and set_indentation_for_print_function.
    number_of_spaces_of_current_function=`echo ${#FUNCNAME[@]} | awk \
        -v default_number_of_indentation_spaces="$default_number_of_indentation_spaces" '
        { print ($1-3)*default_number_of_indentation_spaces}
        '`
    #                            actual indent is sum of default_number_of_indentation_spaces + number_of_spaces_of_current_function
    let INDENT=$number_of_spaces_of_current_function+$default_number_of_indentation_spaces
}
print () { # print anything inside function with proper indent
    set_indentation_for_print_function
    awk -v l="${INDENT:=0}" 'BEGIN {for(i=1;i<=l;i++) printf(" ")}' # print INDENT spaces before echo
    echo $@
}
_ () { # decorator itself, prints funcname: args
    set_indentation_for_print_function
    let INDENT=$INDENT-$default_number_of_indentation_spaces # we remove def_number here, because function has to be right from usual print
    awk -v l="${INDENT:=0}" 'BEGIN {for(i=1;i<=l;i++) printf(" ")}' # print INDENT spaces before echo
    #tput setaf 0 && tput bold # uncomment this for grey color of decorator
    [ $INDENT -ne 0 ] && echo "${FUNCNAME[1]}: $@" # here we avoid situation where decorator is used inside the body of script and not in the function
    #tput sgr0 # resets grey color
}

ฉันพยายามที่จะนำมากที่สุดเท่าที่เป็นไปได้ในการแสดงความคิดเห็น แต่ที่นี่ยังเป็นคำอธิบาย: ฉันจะใช้ฟังก์ชั่นเป็นมัณฑนากรหนึ่งที่ฉันใส่หลังจากการประกาศของทุกฟังก์ชั่น:_ () foo () { _ฟังก์ชั่นนี้จะพิมพ์ชื่อฟังก์ชั่นด้วยการเยื้องที่เหมาะสมขึ้นอยู่กับฟังก์ชั่นลึกในฟังก์ชั่นอื่น ๆ (เป็นเยื้องเริ่มต้นฉันใช้จำนวนช่องว่าง 4 จำนวน) ฉันมักจะพิมพ์เป็นสีเทาเพื่อแยกสิ่งนี้จากการพิมพ์ปกติ หากจำเป็นต้องมีการตกแต่งฟังก์ชั่นด้วยอาร์กิวเมนต์หรือไม่มีใครสามารถแก้ไขบรรทัดก่อนหน้าในฟังก์ชันมัณฑนากร

เพื่อที่จะพิมพ์บางอย่างภายในฟังก์ชั่นฉันแนะนำprint ()ฟังก์ชั่นที่พิมพ์ทุกอย่างที่ผ่านไปด้วยการเยื้องที่เหมาะสม

ฟังก์ชั่นทำหน้าที่set_indentation_for_print_functionตรงกับการคำนวณการเยื้องจาก${FUNCNAME[@]}อาเรย์

วิธีนี้มีข้อบกพร่องบางอย่างตัวอย่างเช่นไม่สามารถส่งตัวเลือกให้printชอบechoเช่น-nหรือ-eถ้าฟังก์ชันส่งคืนค่า 1 ฟังก์ชันจะไม่ได้รับการตกแต่ง และสำหรับอาร์กิวเมนต์ที่ส่งผ่านไปยังprintมากกว่าความกว้างของเทอร์มินัลซึ่งจะถูกห่อบนหน้าจอเราจะไม่เห็นการเยื้องสำหรับการพันบรรทัด

วิธีที่ยอดเยี่ยมในการใช้เครื่องมือตกแต่งเหล่านี้คือการใส่ลงในไฟล์แยกกันและในแต่ละสคริปต์ใหม่เพื่อส่งไฟล์source ~/script/hand_made_bash_functions.shนี้

ฉันคิดว่าวิธีที่ดีที่สุดในการรวมฟังก์ชั่นมัณฑนากรไว้ใน bash คือการเขียนมัณฑนากรไว้ในเนื้อหาของแต่ละฟังก์ชั่น ฉันคิดว่ามันง่ายกว่ามากในการเขียนฟังก์ชั่นภายในฟังก์ชั่นในทุบตีเพราะมันมีตัวเลือกในการตั้งค่าตัวแปรทั้งหมดทั่วโลกไม่เหมือนในภาษาเชิงวัตถุมาตรฐาน นั่นทำให้มันเหมือนกับว่าคุณกำลังใส่ป้ายกำกับรอบรหัสของคุณในการทุบตี อย่างน้อยก็ช่วยฉันในการดีบักสคริปต์



0

สำหรับฉันนี้รู้สึกเหมือนวิธีที่ง่ายที่สุดในการใช้รูปแบบมัณฑนากรภายในทุบตี

#!/bin/bash

function decorator {
    if [ "${FUNCNAME[0]}" != "${FUNCNAME[2]}" ] ; then
        echo "Turn stuff on"
        #shellcheck disable=2068
        ${@}
        echo "Turn stuff off"
        return 0
    fi
    return 1
}

function highly_decorated {
    echo 'Inside highly decorated, calling decorator function'
    decorator "${FUNCNAME[0]}" "${@}" && return
    echo 'Done calling decorator, do other stuff'
    echo 'other stuff'
}

echo 'Running highly decorated'
# shellcheck disable=SC2119
highly_decorated

เหตุใดคุณจึงปิดการใช้งานคำเตือน ShellCheck เหล่านี้ ดูเหมือนว่าถูกต้อง (แน่นอนว่าคำเตือน SC2068 ควรได้รับการแก้ไขโดยการอ้างอิง"$@")
dimo414

0

ฉันทำมาก (อาจมากเกินไป :)) metaprogramming ใน Bash และพบว่าผู้ตกแต่งที่ประเมินค่ามิได้สำหรับการนำพฤติกรรมกลับมาใช้ใหม่ได้ทันที ห้องสมุดbash-cacheของฉันใช้การตกแต่งเพื่อบันทึกฟังก์ชัน Bash ที่โปร่งใสด้วยพิธีการที่น้อยที่สุด:

my_expensive_function() {
  ...
} && bc::cache my_expensive_function

เห็นได้ชัดว่าbc::cacheกำลังทำมากกว่าแค่การตกแต่ง แต่การตกแต่งพื้นฐานนั้นอาศัยbc::copy_functionการคัดลอกฟังก์ชั่นที่มีอยู่ไปยังชื่อใหม่เพื่อให้ฟังก์ชั่นดั้งเดิมสามารถเขียนทับด้วยมัณฑนากร

# Given a name and an existing function, create a new function called name that
# executes the same commands as the initial function.
bc::copy_function() {
  local function="${1:?Missing function}"
  local new_name="${2:?Missing new function name}"
  declare -F "$function" &> /dev/null || {
    echo "No such function ${function}" >&2; return 1
  }
  eval "$(printf "%s()" "$new_name"; declare -f "$function" | tail -n +2)"
}

นี่คือตัวอย่างง่ายๆของมัณฑนากรที่timeเป็นฟังก์ชั่นการตกแต่งโดยใช้bc::copy_function:

time_decorator() {
  bc::copy_function "$1" "time_dec::${1}" || return
  eval "${1}() { time time_dec::${1} "'"\$@"; }'
}

การสาธิต:

$ slow() { sleep 2; echo done; }

$ time_decorator slow

$ $ slow
done

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