Xin chào! Chào mừng bạn đến với loạt bài hướng dẫn về tập lệnh shell của HowToForge. Nếu bạn muốn đọc các bản phát hành trước của hướng dẫn, vui lòng nhấp vào đây để xem phần 1, phần 2, phần 3 và phần 4 của hướng dẫn. Trong phần này, bạn sẽ học cách cấu trúc hiệu quả các tập lệnh của mình bằng cách tạo các hàm. Đến cuối hướng dẫn này, bạn sẽ có thể biết cách tạo các hàm trong LinuxBash Shell, truyền tham số cho các hàm của mình và trả về một số giá trị từ hàm cho mã chính của mình. Hãy bắt đầu nào!
Tôi đã sử dụng cú pháp thứ hai trong ví dụ của chúng ta. Sau khi tạo hàm myfunction, sau đó hàm này được gọi bằng cách gọi tên hàm của nó tới hàm chính của chúng ta. Hàm chính sẽ ở bất kỳ đâu trong tập lệnh của chúng ta mà không được định nghĩa là một phần của hàm.
Bây giờ chúng ta hãy sắp xếp lại mã của mình để kiểm tra xem các hàm có thể được khai báo ở bất kỳ đâu trong tập lệnh của chúng ta hay không. Hãy xem xét đoạn mã dưới đây:
Dòng 3 trong đoạn mã trên trả về lỗi command not found. Điều này chỉ có nghĩa là:
Ví dụ, hãy xem xét mã bên dưới:
Tập lệnh đang chạy tốt, tuy nhiên, hãy lưu ý rằng các dòng để chấp nhận đầu vào được thực hiện nhiều lần trong mỗi mẫu trong câu lệnh switch của chúng ta.
Chúng tôi đã cải thiện mã của mình bằng cách tạo các tiểu mục inputs và exitPrompt. Nó hoạt động giống hệt với mã trước đó của chúng tôi, tuy nhiên, mã hiện tại của chúng tôi dễ khắc phục sự cố hơn vì nó được cấu trúc đúng cách.
Lưu ý trong ví dụ của chúng tôi, chúng tôi đã thêm các giá trị "Hello" và "World" sau khi chúng tôi gọi myfunction. Các giá trị đó được truyền cho myfunction dưới dạng tham số và được lưu trữ trong một biến cục bộ. Tuy nhiên, không giống như các ngôn ngữ khác, trình thông dịch lưu trữ các giá trị được truyền vào các biến được xác định trước, được đặt tên theo trình tự truyền các tham số, 1 là tên bắt đầu cho đến thứ tự truyền. Lưu ý rằng từ "Hello" được lưu trữ trong biến 1 và giá trị "World" được lưu trữ trong biến 2.
Lưu ý: 1 và 2 trong ví dụ của chúng tôi là các biến cục bộ và do đó, không thể truy cập được từ các phần khác của tập lệnh ngoài hàm nơi các tham số được truyền vào.
Ví dụ:
Echo $1 và echo $2 trong hai dòng cuối cùng của tập lệnh của chúng tôi không hiển thị vì trình thông dịch không nhận ra cả hai biến vì cả hai đều cục bộ đối với myfunction.
Trong ví dụ, chúng ta truyền các tham số int1 và int2 cho hàm add. Tiếp theo, hàm add xử lý nó thông qua dòng sum=$(($1+$2)). Sau đó, giá trị của biến tổng được truyền đến chương trình chính thông qua dòng return $sum. Theo mặc định, các giá trị của $sum sẽ được lưu trữ vào biến mặc định $? Cuối cùng, dòng echo "The result is: " $? sẽ in ra kết quả.
Không giống như các ngôn ngữ lập trình khác, các tập lệnh shell không thể trả về nhiều giá trị từ một hàm. Hãy cùng xem xét ví dụ này:
Ví dụ được đưa ra chuyển đổi một đầu vào được đưa ra thành cả giá trị nhị phân hoặc thập phân bằng lệnh obase và ibase. Dòng $(echo "obase=2;$1"|bc) chuyển đổi một giá trị thập phân được đưa ra thành chữ số nhị phân và lưu trữ nó vào biến bin1. Tiếp theo, chúng tôi hiển thị giá trị của $bin1 bằng lệnh echo.
Ngoài ra, chúng tôi đã chuyển đổi giá trị nhị phân thành thập phân bằng lệnh $(echo "ibase=2;$1"|bc).
Bạn cũng phải nhớ rằng trình thông dịch chỉ có khả năng chấp nhận chữ số nhị phân 8 bit. Bạn sẽ nhập chữ số vượt quá giới hạn 8 bit, nó sẽ tạo ra lỗi tràn và bit quan trọng nhất của chữ số đó sẽ bị loại bỏ.
Chữ số nhị phân 10 bit 1000001010 trả về 10 vì theo quy tắc 8 bit, 2 bit còn lại ở bên phải (bit quan trọng nhất) sẽ bị bỏ qua, do đó, 1000001010 sẽ bằng 00001010, bằng 10. Nếu bạn muốn một hoạt động chấp nhận các chữ số nhị phân vượt quá 8 bit, thì bạn phải tạo mã theo cách thủ công.
Giới thiệu
Một hàm, còn được gọi là một chương trình con trong ngôn ngữ lập trình, là một tập hợp các lệnh thực hiện một tác vụ cụ thể cho một chương trình con chính [1]. Nó cho phép các lập trình viên chia một mã phức tạp và dài thành các phần nhỏ có thể được gọi bất cứ khi nào cần. Mỗi hàm cần được gọi bởi một chương trình con chính để chạy, do đó, nó được cô lập với các phần khác trong mã của bạn và điều này tạo ra một cách dễ dàng để kiểm tra mã. Ngoài ra, các hàm có thể được gọi bất cứ lúc nào và nhiều lần, điều này cho phép bạn sử dụng lại, tối ưu hóa và giảm thiểu mã của mình. Giống như hầu hết các ngôn ngữ lập trình, bash shell cũng hỗ trợ các hàm.Cú pháp chung:
- Cú pháp 1:
Mã:
function function_name { ##### tập lệnh }
- Cú pháp 2:
Mã:
function_name() { #### tập lệnh }
Tạo hàm
Bash hỗ trợ hai cấu trúc cho hàm. Khi sử dụng cú pháp đầu tiên, bạn phải sử dụng từ khóa function, theo sau là tên hàm của bạn và dấu ngoặc đơn mở và đóng cùng dấu ngoặc nhọn để phân tách nội dung của các hàm với hàm chính của bạn. Bạn sẽ thấy cú pháp này quen thuộc nếu bạn có kiến thức nền về PHP vì các hàm trong PHP được khai báo theo cùng một cách. Cú pháp kia chỉ bao gồm tên hàm, dấu ngoặc đơn mở và đóng cùng dấu ngoặc nhọn.
Mã:
#!/bin/bash
myfunction(){
echo "My function works!"
}
myfunction
Tôi đã sử dụng cú pháp thứ hai trong ví dụ của chúng ta. Sau khi tạo hàm myfunction, sau đó hàm này được gọi bằng cách gọi tên hàm của nó tới hàm chính của chúng ta. Hàm chính sẽ ở bất kỳ đâu trong tập lệnh của chúng ta mà không được định nghĩa là một phần của hàm.
Bây giờ chúng ta hãy sắp xếp lại mã của mình để kiểm tra xem các hàm có thể được khai báo ở bất kỳ đâu trong tập lệnh của chúng ta hay không. Hãy xem xét đoạn mã dưới đây:
Mã:
#!/bin/bash
echo "testing my function"
myfunction
myfunction(){
echo "My function works!"
}
Dòng 3 trong đoạn mã trên trả về lỗi command not found. Điều này chỉ có nghĩa là:
Mã:
Hàm chỉ hoạt động nếu được khai báo trước hàm chính của bạn. Trình thông dịch sẽ trả về lỗi nếu bạn khai báo hàm sau hàm chính.
Cấu trúc lại mã bằng hàm
Một trong những tính năng tốt nhất của hàm là có thể sử dụng lại mã. Khi một thủ tục yêu cầu thực hiện lệnh nhiều lần nhưng không thể cấu trúc bằng các câu lệnh lặp thì hàm có thể là giải pháp.Ví dụ, hãy xem xét mã bên dưới:
Mã:
#!/bin/bash
while(true)
do
clear
printf "Chọn từ các phép toán sau: \n"
printf "[a]ddition\n[b]Subtraction\n[c]Multiplication\n[d]Division\n"
printf "##################################\n"
read -p "Lựa chọn của bạn: " choice
case $choice in
[aA])
read -p "Nhập số nguyên đầu tiên: " int1
read -p "Nhập số nguyên thứ hai: " int2
res=$((int1+int2))
;;
[bB])
read -p "Nhập số nguyên đầu tiên: " int1
read -p "Nhập số nguyên thứ hai: " int2
res=$((int1-int2))
;;
[cC])
read -p "Nhập số nguyên đầu tiên: " int1
read -p "Nhập số nguyên thứ hai: " int2
res=$((int1*int2))
;;
[dD])
read -p "Nhập số nguyên đầu tiên: " int1
read -p "Nhập số nguyên thứ hai: " int2
res=$((int1/int2))
;;
*)
res=0
echo "lựa chọn sai!"
esac
echo "Kết quả là: " $res
read -p "Bạn có muốn tiếp tục không? [có] hoặc [không]: " ans
if [ $ans == 'n' ]
then
echo "Đang thoát khỏi tập lệnh. Chúc một ngày tốt lành!"
break
else
continue
fi
done
Tập lệnh đang chạy tốt, tuy nhiên, hãy lưu ý rằng các dòng để chấp nhận đầu vào được thực hiện nhiều lần trong mỗi mẫu trong câu lệnh switch của chúng ta.
Mã:
#!/bin/bash
inputs(){
read -p "Nhập số nguyên đầu tiên: " int1
read -p "Nhập số nguyên thứ hai: " int2
}
exitPrompt(){
read -p "Bạn có muốn tiếp tục không? [có] hoặc [không]: " ans
if [ $ans == 'n' ]
then
echo "Đang thoát khỏi tập lệnh. Chúc một ngày tốt lành!"
break
else
continue
fi
}
while(true)
do
clear
printf "Chọn từ các phép toán sau: \n"
printf "[a]Phép cộng\n[b]Phép trừ\n[c]Phép nhân\n[d]Phép chia\n"
printf "#################################\n"
read -p "Lựa chọn của bạn: " choice
case $choice in
[aA])
inputs
res=$((int1+int2))
;;
[bB])
inputs
res=$((int1-int2))
;;
[cC])
inputs
res=$((int1*int2))
;;
[dD])
inputs
res=$((int1/int2))
;;
*)
res=0
echo "lựa chọn sai!"
esac
echo "Kết quả là: " $res
exitPrompt
done
Chúng tôi đã cải thiện mã của mình bằng cách tạo các tiểu mục inputs và exitPrompt. Nó hoạt động giống hệt với mã trước đó của chúng tôi, tuy nhiên, mã hiện tại của chúng tôi dễ khắc phục sự cố hơn vì nó được cấu trúc đúng cách.
Truyền tham số cho các hàm
Giống như hầu hết các ngôn ngữ lập trình, bạn có thể truyền tham số và xử lý dữ liệu đó trong các hàm trong bash. Mã bên dưới cho thấy quy trình về cách truyền giá trị trong tập lệnh shell:
Mã:
#!/bin/bash
myfunction(){
echo $1
echo $2
}
myfunction "Hello" "World"
Lưu ý trong ví dụ của chúng tôi, chúng tôi đã thêm các giá trị "Hello" và "World" sau khi chúng tôi gọi myfunction. Các giá trị đó được truyền cho myfunction dưới dạng tham số và được lưu trữ trong một biến cục bộ. Tuy nhiên, không giống như các ngôn ngữ khác, trình thông dịch lưu trữ các giá trị được truyền vào các biến được xác định trước, được đặt tên theo trình tự truyền các tham số, 1 là tên bắt đầu cho đến thứ tự truyền. Lưu ý rằng từ "Hello" được lưu trữ trong biến 1 và giá trị "World" được lưu trữ trong biến 2.
Lưu ý: 1 và 2 trong ví dụ của chúng tôi là các biến cục bộ và do đó, không thể truy cập được từ các phần khác của tập lệnh ngoài hàm nơi các tham số được truyền vào.
Ví dụ:
Mã:
#!/bin/bash
myfunction(){
echo $1
echo $2
}
myfunction "Hello" "World"
echo $1
echo $2
Echo $1 và echo $2 trong hai dòng cuối cùng của tập lệnh của chúng tôi không hiển thị vì trình thông dịch không nhận ra cả hai biến vì cả hai đều cục bộ đối với myfunction.
Trả về giá trị từ hàm
Ngoài việc tạo hàm và truyền tham số cho hàm, hàm bash có thể truyền giá trị của biến cục bộ của hàm vào hàm chính bằng cách sử dụng từ khóa return. Các giá trị trả về sau đó được lưu trữ vào biến mặc định $? Ví dụ, hãy xem xét đoạn mã sau:
Mã:
#!/bin/bash
add(){
sum=$(($1+$2))
return $sum
}
read -p "Nhập một số nguyên: " int1
read -p "Nhập một số nguyên: " int2
add $int1 $int2
echo "Kết quả là: " $?
Trong ví dụ, chúng ta truyền các tham số int1 và int2 cho hàm add. Tiếp theo, hàm add xử lý nó thông qua dòng sum=$(($1+$2)). Sau đó, giá trị của biến tổng được truyền đến chương trình chính thông qua dòng return $sum. Theo mặc định, các giá trị của $sum sẽ được lưu trữ vào biến mặc định $? Cuối cùng, dòng echo "The result is: " $? sẽ in ra kết quả.
Mã:
Lưu ý: Các tập lệnh shell chỉ có thể trả về một giá trị duy nhất.
Mã:
#!/bin/bash
add(){
sum=$(($1+$2))
dif=$(($1-$2))
return $sum
}
read -p "Nhập một số nguyên: " int1
read -p "Nhập một số nguyên: " int2
add $int1 $int2
echo "Kết quả là: " $?
echo "Kết quả là: " $?
Tóm lại
Chúng ta hãy xem một ví dụ khác sử dụng các hàm, truyền tham số cho hàm và trả về giá trị.
Mã:
#!/bin/bash
######################
#Tác giả: HowtoForge #
#######################
clear(){
clear
}
bin(){
bin1=$(echo "obase=2;$1"|bc)
echo $bin1
}
dec(){
dec1=$(echo "ibase=2;$1"|bc)
return $dec1
}
########Main#########
printf "Chọn từ các thao tác sau:\n[1]Chuyển đổi thập phân sang nhị phân\n"
printf "[2]Nhị phân sang thập phân Conversion\n"
read -p "Lựa chọn của bạn: " op
case $op in
1)
read -p "Nhập số nguyên: " int
bin $int
;;
2)
read -p "Nhập số nhị phân: " int
dec $int
echo "Số thập phân tương đương của $int là $?"
;;
*)
echo "Lựa chọn sai!"
esac
Ví dụ được đưa ra chuyển đổi một đầu vào được đưa ra thành cả giá trị nhị phân hoặc thập phân bằng lệnh obase và ibase. Dòng $(echo "obase=2;$1"|bc) chuyển đổi một giá trị thập phân được đưa ra thành chữ số nhị phân và lưu trữ nó vào biến bin1. Tiếp theo, chúng tôi hiển thị giá trị của $bin1 bằng lệnh echo.
Mã:
Lưu ý: Tốt hơn là sử dụng echo trực tiếp khi chuyển đổi từ thập phân sang nhị phân vì khi bạn trả về lệnh để truyền giá trị nhị phân, bash sẽ chuyển đổi giá trị nhị phân thành thập phân trước khi trả về.
Bạn cũng phải nhớ rằng trình thông dịch chỉ có khả năng chấp nhận chữ số nhị phân 8 bit. Bạn sẽ nhập chữ số vượt quá giới hạn 8 bit, nó sẽ tạo ra lỗi tràn và bit quan trọng nhất của chữ số đó sẽ bị loại bỏ.
Chữ số nhị phân 10 bit 1000001010 trả về 10 vì theo quy tắc 8 bit, 2 bit còn lại ở bên phải (bit quan trọng nhất) sẽ bị bỏ qua, do đó, 1000001010 sẽ bằng 00001010, bằng 10. Nếu bạn muốn một hoạt động chấp nhận các chữ số nhị phân vượt quá 8 bit, thì bạn phải tạo mã theo cách thủ công.