Trong lập trình mệnh lệnh điển hình , bạn viết các chuỗi hướng dẫn và chúng được thực hiện lần lượt, với luồng điều khiển rõ ràng. Ví dụ:
if [ -f file1 ]; then # If file1 exists ...
cp file1 file2 # ... create file2 as a copy of a file1
fi
v.v.
Như có thể thấy từ ví dụ này, trong lập trình mệnh lệnh, bạn tuân theo luồng thực thi khá dễ dàng, luôn luôn làm việc theo cách của bạn từ bất kỳ dòng mã đã cho nào để xác định bối cảnh thực thi của nó, biết rằng bất kỳ lệnh nào bạn đưa ra sẽ được thực hiện do kết quả của chúng vị trí trong luồng (hoặc vị trí trang web cuộc gọi của họ, nếu bạn đang viết chức năng).
Cách gọi lại thay đổi dòng chảy
Khi bạn sử dụng các cuộc gọi lại, thay vì đặt việc sử dụng một bộ hướng dẫn, về mặt địa lý, bạn sẽ mô tả khi nào nên gọi nó. Các ví dụ điển hình trong các môi trường lập trình khác là các trường hợp như tải xuống tài nguyên này và khi quá trình tải xuống hoàn tất, hãy gọi đây là cuộc gọi lại. Bash không có cấu trúc gọi lại chung loại này, nhưng nó có các cuộc gọi lại, để xử lý lỗi và một vài tình huống khác; ví dụ (trước tiên người ta phải hiểu thay thế lệnh và chế độ thoát Bash để hiểu ví dụ đó):
#!/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
Nếu bạn muốn tự mình thử điều này, hãy lưu phần trên vào một tệp, giả sử cleanUpOnExit.sh
, làm cho nó có thể thực thi được và chạy nó:
chmod 755 cleanUpOnExit.sh
./cleanUpOnExit.sh
Mã của tôi ở đây không bao giờ gọi cleanup
hàm một cách rõ ràng ; nó nói với Bash khi nào nên gọi nó, bằng cách sử dụng trap cleanup EXIT
, tức là Bash thân yêu Bash, vui lòng chạy cleanup
lệnh khi bạn thoát ra (và cleanup
tình cờ là một hàm tôi đã xác định trước đó, nhưng nó có thể là bất cứ điều gì Bash hiểu). Bash hỗ trợ điều này cho tất cả các tín hiệu không nghiêm trọng, thoát, lỗi lệnh và gỡ lỗi chung (bạn có thể chỉ định một cuộc gọi lại được chạy trước mỗi lệnh). Hàm gọi lại ở đây là cleanup
hàm, được gọi là trở lại bởi Bash bởi Bash ngay trước khi shell thoát ra.
Bạn có thể sử dụng khả năng của Bash để đánh giá các tham số shell dưới dạng các lệnh, để xây dựng khung theo hướng gọi lại; điều đó hơi vượt quá phạm vi của câu trả lời này, và có lẽ sẽ gây ra nhiều nhầm lẫn hơn bằng cách đề xuất rằng việc truyền các hàm xung quanh luôn liên quan đến các cuộc gọi lại. Xem Bash: truyền một hàm làm tham số cho một số ví dụ về chức năng cơ bản. Ý tưởng ở đây, như với các cuộc gọi lại xử lý sự kiện, là các hàm có thể lấy dữ liệu làm tham số, nhưng cũng có các hàm khác - điều này cho phép người gọi cung cấp hành vi cũng như dữ liệu. Một ví dụ đơn giản của phương pháp này có thể trông giống như
#!/bin/bash
doonall() {
command="$1"
shift
for arg; do
"${command}" "${arg}"
done
}
backup() {
mkdir -p ~/backup
cp "$1" ~/backup
}
doonall backup "$@"
(Tôi biết điều này hơi vô dụng vì cp
có thể xử lý nhiều tệp, nó chỉ mang tính minh họa.)
Ở đây chúng tôi tạo ra một chức năng, doonall
lấy một lệnh khác, được đưa ra làm tham số và áp dụng nó cho các tham số còn lại của nó; sau đó chúng ta sử dụng hàm đó để gọi backup
hàm trên tất cả các tham số được cung cấp cho tập lệnh. Kết quả là một tập lệnh sao chép tất cả các đối số của nó, từng cái một, vào một thư mục sao lưu.
Cách tiếp cận này cho phép các chức năng được viết với các trách nhiệm duy nhất: doonall
trách nhiệm của chúng là chạy một cái gì đó trên tất cả các đối số của nó, từng cái một; backup
Trách nhiệm của bạn là tạo một bản sao của đối số (duy nhất) của nó trong một thư mục sao lưu. Cả haidoonall
và backup
có thể được sử dụng trong các bối cảnh khác, cho phép sử dụng lại nhiều mã hơn, kiểm tra tốt hơn, v.v.
Trong trường hợp này, gọi lại là backup
chức năng, mà chúng tôi nóidoonall
với Gọi lại gọi lại trên mỗi đối số khác của nó - chúng ta cung cấp doonall
hành vi (đối số đầu tiên của nó) cũng như dữ liệu (các đối số còn lại).
(Lưu ý rằng trong trường hợp sử dụng được thể hiện trong ví dụ thứ hai, tôi sẽ không sử dụng thuật ngữ Gọi lại chính mình, nhưng đó có lẽ là thói quen xuất phát từ các ngôn ngữ tôi sử dụng. Tôi nghĩ rằng đây là việc truyền các hàm hoặc lambdas xung quanh , thay vì đăng ký cuộc gọi lại trong một hệ thống hướng sự kiện.)