แนวคิด "การโทรกลับ" ของการเขียนโปรแกรมมีอยู่ใน Bash หรือไม่


21

สองสามครั้งเมื่อฉันอ่านเกี่ยวกับการเขียนโปรแกรมฉันพบแนวคิด "การโทรกลับ"

สนุกฉันไม่เคยพบคำอธิบายที่ฉันสามารถเรียกว่า "การสอน" หรือ "ชัดเจน" สำหรับคำนี้ "ฟังก์ชั่นการโทรกลับ" (คำอธิบายเกือบทั้งหมดที่ฉันอ่านดูเหมือนว่าฉันแตกต่างจากที่อื่นมากพอและฉันรู้สึกสับสน)

แนวคิด "การโทรกลับ" ของการเขียนโปรแกรมมีอยู่ใน Bash หรือไม่ ถ้าเป็นเช่นนั้นโปรดตอบด้วยตัวอย่างง่ายๆแบบ Bash


2
"callback" เป็นแนวคิดจริงหรือว่าเป็น "ฟังก์ชันชั้นหนึ่ง" หรือไม่?
Cedric H.

คุณอาจพบว่าdeclarative.bashน่าสนใจเนื่องจากเฟรมเวิร์กที่ยกระดับฟังก์ชั่นที่กำหนดค่าให้เรียกใช้อย่างชัดเจนเมื่อต้องการค่าที่กำหนด
Charles Duffy

อีกกรอบที่เกี่ยวข้อง: bashup / เหตุการณ์ เอกสารประกอบไปด้วยการสาธิตการใช้งานการโทรกลับจำนวนมากเช่นการตรวจสอบความถูกต้องการค้นหาเป็นต้น
PJ Eby

1
@CedricH โหวตให้คุณ "is callback" เป็นแนวคิดจริงหรือ "ฟังก์ชั่นชั้นหนึ่ง"? "เป็นคำถามที่ดีที่จะถามเป็นคำถามอื่นหรือไม่
prosody-Gab Vereable Context

ฉันเข้าใจการโทรกลับเพื่อหมายถึง "ฟังก์ชั่นที่ถูกเรียกกลับหลังจากเหตุการณ์ที่กำหนดถูกกระตุ้น ถูกต้องไหม
JohnDoea

คำตอบ:


44

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

if [ -f file1 ]; then   # If file1 exists ...
    cp file1 file2      # ... create file2 as a copy of a file1
fi

เป็นต้น

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

วิธีการเรียกกลับเปลี่ยนกระแส

เมื่อคุณใช้การเรียกกลับแทนที่จะวางชุดคำแนะนำ "ทางภูมิศาสตร์" คุณจะอธิบายเมื่อควรเรียกใช้ ตัวอย่างทั่วไปในสภาพแวดล้อมการเขียนโปรแกรมอื่น ๆ เป็นกรณีเช่น "ดาวน์โหลดทรัพยากรนี้และเมื่อการดาวน์โหลดเสร็จสมบูรณ์แล้วให้เรียกใช้การเรียกกลับนี้" Bash ไม่มีโครงสร้างการเรียกกลับแบบทั่วไป แต่มีการเรียกกลับสำหรับการจัดการข้อผิดพลาดและสถานการณ์อื่น ๆ ตัวอย่างเช่น (ก่อนอื่นต้องเข้าใจการแทนที่คำสั่งและโหมด Bash exit ก่อนเพื่อทำความเข้าใจตัวอย่างนั้น):

#!/bin/bash

scripttmp=$(mktemp -d)           # Create a temporary directory (these will usually be created under /tmp or /var/tmp/)

cleanup() {                      # Declare a cleanup function
    rm -rf "${scripttmp}"        # ... which deletes the temporary directory we just created
}

trap cleanup EXIT                # Ask Bash to call cleanup on exit

หากคุณต้องการลองด้วยตัวคุณเองให้บันทึกข้างต้นลงในไฟล์โดยบอกว่าcleanUpOnExit.shทำให้มันใช้งานได้และเรียกใช้:

chmod 755 cleanUpOnExit.sh
./cleanUpOnExit.sh

รหัสของฉันที่นี่ไม่เคยเรียกcleanupฟังก์ชั่นอย่างชัดเจน; มันบอกทุบตีเมื่อจะเรียกมันว่าใช้trap cleanup EXIT, เช่น “รักทุบตีโปรดเรียกใช้cleanupคำสั่งเมื่อคุณออก” (และcleanupเกิดขึ้นเป็นฟังก์ชั่นที่กำหนดไว้ก่อนหน้านี้ผม แต่มันจะเป็นอะไรทุบตีเข้าใจ) Bash รองรับสิ่งนี้สำหรับสัญญาณที่ไม่ร้ายแรง, ออก, ความล้มเหลวของคำสั่งและการดีบักทั่วไป (คุณสามารถระบุการเรียกกลับที่ทำงานก่อนทุกคำสั่ง) การเรียกกลับที่นี่คือcleanupฟังก์ชั่นซึ่งเป็น“ เรียกกลับ” โดย Bash ก่อนที่เชลล์จะออก

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

#!/bin/bash

doonall() {
    command="$1"
    shift
    for arg; do
        "${command}" "${arg}"
    done
}

backup() {
    mkdir -p ~/backup
    cp "$1" ~/backup
}

doonall backup "$@"

(ฉันรู้ว่านี่มันไร้ประโยชน์นิดหน่อยเพราะcpสามารถจัดการกับไฟล์หลาย ๆ ไฟล์มันเป็นเพียงภาพประกอบเท่านั้น)

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

วิธีการแบบนี้ช่วยให้ฟังก์ชั่นสามารถเขียนด้วยความรับผิดชอบเดียว: doonallความรับผิดชอบของคือการเรียกใช้บางสิ่งบางอย่างในทุกข้อโต้แย้งของมันทีละครั้ง; backupความรับผิดชอบของคือการทำสำเนาของอาร์กิวเมนต์ (แต่เพียงผู้เดียว) ในไดเรกทอรีสำรอง ทั้งสองdoonallและbackupสามารถใช้ในบริบทอื่น ๆ ซึ่งอนุญาตให้ใช้รหัสได้มากขึ้นการทดสอบที่ดีขึ้น ฯลฯ

ในกรณีนี้การเรียกกลับเป็นbackupฟังก์ชั่นซึ่งเราบอกdoonallให้ "โทรกลับ" ในแต่ละข้อโต้แย้งอื่น ๆ ของมัน - เราให้doonallกับพฤติกรรม (อาร์กิวเมนต์แรกของมัน) เช่นเดียวกับข้อมูล (อาร์กิวเมนต์ที่เหลือ)

(โปรดทราบว่าในรูปแบบการใช้งานที่แสดงในตัวอย่างที่สองฉันจะไม่ใช้คำว่า "โทรกลับ" ด้วยตัวเอง แต่นั่นอาจเป็นนิสัยที่เกิดจากภาษาที่ฉันใช้ฉันคิดว่านี่เป็นหน้าที่ส่งผ่านหรือ lambdas รอบ ๆ แทนที่จะลงทะเบียนการเรียกกลับในระบบเชิงเหตุการณ์)


25

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

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

อีกตัวอย่างของการติดต่อกลับคือการ-execกระทำของfindคำสั่ง งานของfindคำสั่งคือการสำรวจไดเรกทอรีซ้ำและประมวลผลแต่ละไฟล์ในทางกลับกัน โดยค่าเริ่มต้นการประมวลผลคือการพิมพ์ชื่อไฟล์ (โดยนัย-print) แต่ด้วย-execการประมวลผลคือการเรียกใช้คำสั่งที่คุณระบุ สิ่งนี้เหมาะกับคำจำกัดความของการโทรกลับแม้ว่าการโทรกลับจะดำเนินต่อไป แต่ก็ไม่ยืดหยุ่นมากนักเนื่องจากการโทรกลับทำงานในกระบวนการแยกต่างหาก

หากคุณใช้ฟังก์ชั่น find-like คุณสามารถใช้ฟังก์ชัน callback เพื่อโทรหาแต่ละไฟล์ นี่คือฟังก์ชั่น find-like ที่หาง่ายที่สุดที่ใช้ชื่อฟังก์ชั่น (หรือชื่อคำสั่งภายนอก) เป็นอาร์กิวเมนต์และเรียกใช้กับไฟล์ปกติทั้งหมดในไดเรกทอรีปัจจุบันและไดเรกทอรีย่อย ฟังก์ชั่นจะใช้เป็นโทรกลับซึ่งเรียกว่าทุกครั้งที่call_on_regular_filesพบไฟล์ปกติ

shopt -s globstar
call_on_regular_files () {
  declare callback="$1"
  declare file
  for file in **/*; do
    if [[ -f $file ]]; then
      "$callback" "$file"
    fi
  done
}

การโทรกลับไม่เหมือนกันในการเขียนโปรแกรมเชลล์เช่นเดียวกับในสภาพแวดล้อมอื่น ๆ เพราะเชลล์ได้รับการออกแบบมาสำหรับโปรแกรมง่ายๆ การเรียกกลับเป็นเรื่องธรรมดาในสภาพแวดล้อมที่ข้อมูลและการควบคุมการไหลมีแนวโน้มที่จะย้ายไปมาระหว่างส่วนต่าง ๆ ของรหัสที่เขียนและแจกจ่ายอย่างอิสระ: ระบบฐาน, ไลบรารีต่าง ๆ , รหัสแอปพลิเคชัน


1
อธิบายอย่างชัดเจนโดยเฉพาะอย่างยิ่ง
roaima

1
@JohnDoea ฉันคิดว่าความคิดนี้คือมันง่ายขึ้นเป็นพิเศษเพราะไม่ใช่ฟังก์ชั่นที่คุณเขียนจริงๆ แต่บางทีอาจจะเป็นตัวอย่างได้ง่ายจะเป็นสิ่งที่มีรายชื่อยากรหัสในการเรียกใช้โทรกลับเมื่อ: foreach_server() { declare callback="$1"; declare server; for server in 192.168.0.1 192.168.0.2 192.168.0.3; do "$callback" "$server"; done; }ที่คุณสามารถเรียกใช้เป็นforeach_server echo, foreach_server nslookupฯลฯdeclare callback="$1"เป็นเรื่องง่าย ๆ ตามที่ได้รับว่า: โทรกลับจะต้องมีการส่งผ่านไปในที่ใดที่หนึ่ง หรือไม่ใช่การติดต่อกลับ
IMSoP

4
'การเรียกกลับคือเมื่อรหัสที่คุณเขียนนั้นถูกเรียกจากรหัสที่คุณไม่ได้เขียน' มันผิด คุณสามารถเขียนสิ่งที่ทำให้ async ที่ไม่มีการบล็อกทำงานและเรียกใช้เมื่อมีการติดต่อกลับที่จะทำงานเมื่อเสร็จ ไม่มีอะไรเกี่ยวข้องกับผู้ที่เขียนรหัส
mikemaccana

5
@mikemaccana แน่นอนว่าเป็นไปได้ว่าคนคนเดียวกันเขียนโค้ดสองส่วน แต่ไม่ใช่กรณีทั่วไป ฉันอธิบายพื้นฐานของแนวคิดไม่ใช่การให้คำจำกัดความที่เป็นทางการ หากคุณอธิบายทุกมุมกรณีมันเป็นเรื่องยากที่จะถ่ายทอดพื้นฐาน
Gilles 'หยุดชั่วร้าย'

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

7

"callbacks" เป็นเพียงฟังก์ชันที่ส่งผ่านเป็นอาร์กิวเมนต์ไปยังฟังก์ชันอื่น

ที่ระดับเชลล์นั่นหมายถึงสคริปต์ / ฟังก์ชั่น / คำสั่งที่ส่งเป็นอาร์กิวเมนต์ไปยังสคริปต์ / ฟังก์ชั่น / คำสั่งอื่น ๆ

ตอนนี้สำหรับตัวอย่างง่ายๆพิจารณาสคริปต์ต่อไปนี้:

$ cat ~/w/bin/x
#! /bin/bash
cmd=$1; shift
case $1 in *%*) flt=${1//\%/\'%s\'};; *) flt="$1 '%s'";; esac; shift
q="'\\''"; f=${flt//\\/'\\'}; p=`printf "<($f) " "${@//\'/$q}"`
eval "$cmd" "$p"

มีบทสรุป

x command filter [file ...]

จะใช้filterกับแต่ละfileอาร์กิวเมนต์จากนั้นเรียกcommandด้วยผลลัพธ์ของตัวกรองเป็นอาร์กิวเมนต์

ตัวอย่างเช่น

x diff zcat a.gz b.bz   # diff gzipped files
x diff3 zcat a.gz b.gz c.gz   # same with three-way diff
x diff hd a b  # hex diff of binary files
x diff 'zcat % | sort -u' a.gz b.gz  # first uncompress the files, then sort+uniq them, then compare them
x 'comm -12' sort a b  # find common lines in unsorted files

นี่ใกล้กับสิ่งที่คุณสามารถทำได้ในเสียงกระเพื่อม (เพียงล้อเล่น ;-))

บางคนยืนยันที่จะ จำกัด คำว่า "การเรียกกลับ" เป็น "ตัวจัดการเหตุการณ์" และ / หรือ "ปิด" (ฟังก์ชัน + data / environment tuple) นี่คือความหมายที่ยอมรับกันโดยทั่วไป และเหตุผลหนึ่งที่ว่าทำไม "การเรียกกลับ" ในความรู้สึกแคบ ๆ นั้นไม่ค่อยมีประโยชน์อะไรในเชลล์เพราะความสามารถในการเขียนโปรแกรมแบบขนาน + ขนาน + + แบบไดนามิกนั้นทรงพลังมากกว่าและคุณจ่ายมันในแง่ของประสิทธิภาพแล้ว พยายามที่จะใช้เปลือกเป็นรุ่นอุ้ยอ้ายของหรือperlpython


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

1
@ Joe ถ้ามันตกลงเพื่อการทำงานที่มีเพียงสองใส่ไฟล์และไม่มีการแก้ไขในตัวกรองสิ่งที่ทั้งอาจจะลดลงไปที่:% cmd=$1; shift; flt=$1; shift; $cmd <($flt "$1") <($flt "$2")แต่นั่นมีประโยชน์น้อยกว่ามากและเป็นตัวอย่างที่ดี
mosvy

1
หรือดียิ่งขึ้น$1 <($2 "$3") <($2 "$4")
mosvy

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

4

ชนิดของ

วิธีง่าย ๆ ในการใช้การติดต่อกลับใน bash คือการยอมรับชื่อของโปรแกรมเป็นพารามิเตอร์ซึ่งทำหน้าที่เป็น "ฟังก์ชันการเรียกกลับ"

# This is script worker.sh accepts a callback in $1
cb="$1"
....
# Execute the call back, passing 3 parameters
$cb foo bar baz

สิ่งนี้จะถูกใช้เช่นนี้:

# Invokes mycb.sh as a callback
worker.sh mycb.sh

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

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


3

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

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

โปรดทราบว่าสิ่งที่คล้ายกันเกิดขึ้นเมื่อฟังก์ชันส่งออก เชลล์ที่อิมพอร์ตฟังก์ชันอาจมีเฟรมเวิร์กพร้อมและกำลังรอคำจำกัดความของฟังก์ชันเพื่อทำให้เชลล์ทำงาน การส่งออกฟังก์ชั่นมีอยู่ใน Bash และก่อให้เกิดปัญหาร้ายแรงก่อนหน้านี้ btw (ที่เรียกว่า Shellshock):

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


2

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

cron

Cron ช่วยให้คุณระบุปฏิบัติการ (ไบนารีหรือสคริปต์) ที่โปรแกรม cron จะโทรกลับเมื่อเงื่อนไขตรงตามเงื่อนไข (สเปคเวลา)

doEveryDay.shสมมติว่าคุณมีสคริปต์ที่เรียกว่า วิธีการเขียนสคริปต์แบบไม่ติดต่อกลับคือ:

#! /bin/bash
while true; do
    doSomething
    sleep $TWENTY_FOUR_HOURS
done

วิธีโทรกลับเพื่อเขียนเป็นเพียง:

#! /bin/bash
doSomething

จากนั้นใน crontab คุณต้องตั้งค่าบางอย่าง

0 0 * * *     doEveryDay.sh

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


ตอนนี้ให้พิจารณาว่าคุณจะเขียนโค้ดนี้ด้วยวิธีทุบตีได้อย่างไร

คุณจะใช้สคริปต์ / ฟังก์ชันอื่นในการทุบตีอย่างไร

ลองเขียนฟังก์ชั่น:

function every24hours () {
    CALLBACK=$1 ;# assume the only argument passed is
                 # something we can "call"/execute
    while true; do
        $CALLBACK ;# simply call the callback
        sleep $TWENTY_FOUR_HOURS
    done
}

ตอนนี้คุณได้สร้างฟังก์ชั่นที่ยอมรับการโทรกลับแล้ว คุณสามารถเรียกมันว่า:

# "ping" google website every day
every24hours 'curl google.com'

แน่นอนฟังก์ชั่นทุก ๆ 24 ชั่วโมงไม่เคยกลับมา Bash นั้นมีความโดดเด่นเล็กน้อยที่เราสามารถทำให้มันเป็นแบบอะซิงโครนัสและวางกระบวนการโดยการต่อท้าย&:

every24hours 'curl google.com' &

หากคุณไม่ต้องการให้สิ่งนี้เป็นฟังก์ชันคุณสามารถทำสิ่งนี้เป็นสคริปต์แทน:

#every24hours.sh
CALLBACK=$1 ;# assume the only argument passed is
               # something we can "call"/execute
while true; do
    $CALLBACK ;# simply call the callback
    sleep $TWENTY_FOUR_HOURS
done

อย่างที่คุณเห็นการโทรกลับด้วย bash นั้นเป็นเรื่องเล็กน้อย มันเป็นเพียง:

CALLBACK_SCRIPT=$3 ;# or some other 
                    # argument to 
                    # function/script

และการโทรกลับเป็นเพียง:

$SOME_CALLBACK_FUNCTION_OR_SCRIPT

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


ตัวอย่างอื่น ๆ ของโปรแกรม / สคริปต์ที่ยอมรับการเรียกกลับรวมwatchและfind(เมื่อใช้กับ-execพารามิเตอร์)
slebetman

0

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

นี่คือตัวอย่างของกับดักสัญญาณการใช้ประโยชน์โทรกลับง่าย แต่ใช้งานง่าย

ขั้นแรกให้สร้างสคริปต์ที่ใช้งานการติดต่อกลับ:

#!/bin/bash

myCallback() {
    echo "I've been called at $(date +%Y%m%dT%H%M%S)"
}

# Set the handler
trap myCallback SIGUSR1

# Main loop. Does nothing useful, essentially waits
while true; do
    read foo
done

จากนั้นรันสคริปต์ในเทอร์มินัลเดียว:

$ ./callback-example

และอีกอันหนึ่งส่งUSR1สัญญาณไปยังกระบวนการเชลล์

$ pkill -USR1 callback-example

สัญญาณที่ส่งแต่ละอันควรกระตุ้นการแสดงของบรรทัดเช่นเดียวกับสัญญาณเหล่านี้ในเทอร์มินัลแรก:

I've been called at 20180925T003515
I've been called at 20180925T003517

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

ยกตัวอย่างเช่นคุณลักษณะนี้ได้รับอนุญาต X11 / Xt / Motif เรียกกลับสไตล์เครื่องมือกราฟิกที่จะดำเนินการในรุ่นเก่าที่ส่วนขยายกราฟิกรวมเรียกว่าksh dtkshดูคู่มือ DKSH

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