Các Lược Giảng Chuyên Sâu về Sử Dụng Văn Lệnh BASH trong Linux/Bài 3

Từ Thư viện Khoa học VLOS
Bước tới: chuyển hướng, tìm kiếm

Loạt bài "Các Lược Giảng Chuyên Sâu về Sử Dụng Văn Lệnh BASH trong Linux" của tác giả Làng Đậu giữ bản quyền 2006. Người đọc chỉ được sử dụng cho mụch đích học tập hay giảng dạy cho cá nhân. Cấm mọi hình thức sao chép, đăng lại, hay in lại nhằm mụch đích mua bán hay trục lợi mà không có sự cho phép chính thức của tác giả. Mọi thông tin về việc phổ biến rộng rãi có tính quảng bá tài liệu này cho mụch đích giáo dục hay các phê bình, đề nghị về nội dung bài giảng xin liên lạc về vo_quang_nhan@yahoo.com

Bài 3: Trọng tâm - Các phép toán, lệnh phân nhánh và vòng lặp

Mục lục

Phép toán dùng với các biểu thức BASH

Trong UNIX/Linux các biểu thức là các mệnh đề logic thường được BASH thẩm định giá trị và từ đó thực thi quyết định của các câu lệnh. Các mệnh đề này thành lập trong việc nối các toán tử (như là các biến các hằng số hay các hàm ...) với các phép toán theo một cú pháp chặt chẽ. Chẳng hạn như các mệnh đề dùng trong câu lệnh if, case hay trong các điều kiện của vòng lặp như là for, while, until .... Các biểu thức này được đặt trong các dấu ngoặc vuông [ ] và có thể nối nhau qua các phép toán logic

Thí dụ:

#correct form 
if [ ! -f myfile ]; then    #if not exist myfile 
        cat myfile 
fi 

#The following expression violate the syntax - missing space between [ and ! sign: #if [! -f myfile ]; then # cat myfile #fi #similar incorrectness: [ ! -f myfile] or [ !-f myfile ]

Lưu ý: Kí tự khoảng trống (space) giữa các dấu ngoặc, các kí hiệu của phép toán, và các toán tử trong BASH quy định rất chặt chẽ. Nếu thiếu hay thừa kí tự khoảng trống này sẽ gây ra lỗi cú pháp

Phép toán thử nghiệm tập tin (file test) trong các biểu thức

Phép toán lên một hạng tử

  [ -b [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] là một thiết bị kiểu khối (như là ổ mềm, cdrom, ổ cứng...) (Xem thêm phụ lục)

[ -c [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] là một thiết bị kiểu kí tự (như là bàn phím, modem, ...) (Xem thêm phụ lục)

[ -d [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] là một thư mục

Thí dụ:

if [ -d /home/Bill ]; then
   echo "directory /home/Bill exist"
if

[ -e [Tên_Tập_Tin] ]  

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] tồn tại

Thí dụ:

if [ -e myfile ]; then 
       echo "my file exists" 
fi

[ -f [Tên_Tập_Tin] ]  

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] là tập tin thông thường

[ -g [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu bit setgid cài lên đó (Xem thêm phụ lục)

[ -k [Tên_Tập_Tin] ]  

Trả về giá trị "true" nếu bit Sticky được cài lên tập tin (Xem thêm phụ lục )

[ -O [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu bạn là chủ tập tin [Tên_Tập_Tin] (Xem thêm phụ lục )

[ -P [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] có kiểu ống dẫn truyền (Xem thêm phụ lục)

[ -L [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] là liên kết mềm (Xem thêm phụ lục)

[ -S [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] là tập tin ổ nối (Xem thêm phụ lục)

[ -r [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] có thuộc tính đọc được (Xem thêm phụ lục)

[ -s [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] có độ dài lớn hơn 0 byte

[ -u [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu bit setuid cài lên đó (Xem thêm phụ lục)

[ -w [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] có thuộc tính cho phép viết (Xem thêm phụ lục)

[ -x [Tên_Tập_Tin] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin] là tập tin thực thi được (Xem thêm phụ lục )

Phép toán lên hai hạng tử

[ [Tên_Tập_Tin_1] -nt [Tên_Tập_Tin_2] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin_1] mới hơn tập tin  [Tên_Tập_Tin_2]

[ [Tên_Tập_Tin_1] -ot [Tên_Tập_Tin_2] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin_1] cũ hơn tập tin  [Tên_Tập_Tin_2]

[ [Tên_Tập_Tin_1] -ef [Tên_Tập_Tin_2] ]

Trả về giá trị "true" nếu tập tin [Tên_Tập_Tin_1][Tên_Tập_Tin_2] cùng liên két tới một tập tin .

Lưu ý:  Nếu [Tên_Tập_Tin_1][Tên_Tập_Tin_2] là hai loại tập tin liên kết khác nhau thì phép toán luôn luôn trả về giá trị "false" (một tệp là liên kết mềm còn tệp kia là liên kết cứng)

Phép toán so sánh trên các số nguyên

Toán tử -eq

  Trả về giá trị "true" nếu cả hai hạng tử bằng nhau.

Thí dụ:

[ $a -eq 5 ]

Toán tử -ne

Trả về giá trị "true" nếu cả hai hạng tử không bằng nhau.

Thí dụ:

 [ $a -ne $b ]

Toán tử -gt 

Trả về giá trị "true" nếu hạng tử thứ nhất lớn hơn hạng tử thứ hai

===Toán tử -lt===   Trả về giá trị "true" nếu hạng tử thứ nhất nhỏ hơn hạng tử thứ hai

Toán tử -le 

Trả về giá trị "true" nếu hạng tử thứ nhất nhỏ hơn hay bằng hạng tử thứ hai

Toán tử -ge 

Trả về giá trị "true" nếu hạng tử thứ nhất lớn hơn hay bằng hạng tử thứ hai

Phép toán so sánh trên các chuỗi kí tự

Các phép toán sau đây dùng để so sánh các biến có nội dung là các string.

Toán tử =

  Trả về giá trị "true" nếu cả hai hạng tử bằng nhau.

Thí dụ:

[ $a = "mystring" ]

Toán tử !=

Trả về giá trị "true" nếu cả hai hạng tử không bằng nhau.  

Thí dụ:

[ $a != $b ]  

Toán tử \<

Trả về giá trị "true" nếu hạng tử thứ nhất nhỏ hơn hạng tử thứ hai theo bảng thứ tự ASCII

Thí dụ:

[ $a \< $b ]

Toán tử \>

Trả về giá trị "true" nếu hạng tử thứ nhất lớn hơn hạng tử thứ hai theo bảng thứ tự ASCII ASCII order

Toán tử -z

Trả về giá trị "true" nếu hạng tử duy nhất này có độ dài bằng 0  

Thí dụ:

if [ -z $mystr ]; then 
   ...
fi

Toán tử -n

Trả về giá trị "true" nếu hạng tử duy nhất này có độ dài lớn hơn 0

Phép toán logic trên các mệnh đề con

Toán tử -a

Phép logic AND

Thí dụ:

if [ $x -eq 10 -a $y -gt 1 ]; then 
    echo "If x equals 10 AND y greater than 1 then" 
fi 

Toán tử -o

phép logic OR

Thí dụ:

if [ $x -eq 10 -o $y -gt 1 ]; then 
    echo "If x equals 10 OR y greater than 1 then" 
fi 

Phép toán logic trên các biểu thức

Có hai toán tử quan trọng đuợc dùng để nối giữa các biểu thức, đó là || (tương đương với toán tử logicl OR), && (tương đương với toán tử logic AND) và ! (tương đương với toán tử NOT). Các phép toán này thường dùng trong các mệnh đề điều kiện như là if, while, for, ...

Thí dụ:

echo -n "Erase this file (y/n) :? "
read ANSWER
if [ "$ANSWER" = "y" ] || [ "$ANSWER" = "yes" ] ; then
    rm -f $file
elif [ "$ANSWER" = "n" ] || [ "$ANSWER" = "no" ] ;  then
    echo "user abort the operation"
   exit 0
else
    echo "Wrong keyed!"
fi

Thí dụ2:

while [ $A -lt 8 ] && [ ! "$B" = "n" ]; do   #while A less than 4 and B is not equal "n" do
   let "A +=2"                               #increasing A (see  next paragraph)
   echo "stop with A=$A (y/n)? "             #asking for stop
   read B                                    #receiving user's decision in B
done
if [ $A -eq 8 ]; then
   echo "A reachs maximum value $A"
fi

Phép toán trên các biến

Phần này trình bày các phép toán làm thay đổi nội dung của các biến

Phép toán lên chuỗi kí tự

Các phép toán trên chuỗi kí tự đã được trình bày chi tiết trong bài 2 phần 6,78

Phép toán số học trên biến nguyên

Để áp dụng các phép toán số học như +, -, * / .. lên các biến có giá trị nguyên thì có thể dùng lệnh

let "[Mệnh_đề_phép_toán_số_học]"  

Tương đương như thế, , cũng có thể dùng

(([Mệnh_đề_phép_toán_số_học])) 

để thực thi các phép toán. Nếu dùng let hay (( )) để gán giá trị nguyên lên biến thì có thể đặt các kí tự trống (space) ở giữa toán tử = mà không sợ bị vi phạm lỗi cú pháp (xem lại cách gán giá trị đơn giản). Ngoài ra, sau khi thi hành các phép tóan số học, let hay (( )) sẽ trả về giá trị 0 nếu giá trị của [Mệnh_đề_phép_toán_số_học] khác không và trả về giá trị 1 cho các trường hợp còn lại.


Toán tử =

Thí dụ:

var1=0
var2=1
let "var1 = $var2"  # remember the assigned variable has NO '$' sign
let "var2= 4"
echo "var1=$var1 and var2=$var2"
((var2 += var2))
echo "var2"

Cộng + 

Thí dụ:

 let "var1 = $var2 + 2 + $var1"

Trừ -

Nhân *

Chia /

Lũy thưà **

Modulo %

Cạnh đó, BASH cũng hỗ trợ một số phép toán tương tự như trong ngôn ngữ C/C++:

Plus-equal (cộng thêm) +=

Thí dụ:

let "var1 +=5"  
# it equivalent to the command: let "var1 = $var1 +5" or command ((var1 += 5))

Minus-equal (trừ thêm) -=

Multiply-equal (nhân thêm)*=

Divide-equal (chia thêm) /=

Modulo-equal (mod thêm) %=

Phép toán nhị phân (Binary oparation) trên các biến nguyên

shift left <<

Đẩy các bit sang trái 1 bit (tương đương với nhân cho 2)

shift left-equal <<=[n]

Đẩy các bit sang trái [n] bit

Thí dụ:

let "myvar <<= 2" #results in myvar is left-shifted 2 bits (or multiplied by 4)

Phép toán nhị phân shift right  >>

Đẩy sang phải 1 bit


Phép toán nhị phân shift right-equal >>=[n]

Phép toán nhị phân AND &

Phép toán nhị phân AND-equal &=

Phép toán nhị phân OR |

Phép toán nhị phân OR-equal |=

Phép toán nhị phân nghịch đảo  ~

Phép toán nhị phân NOT !

Phép toán nhị phân XOR ^

Phép toán nhị phân XOR-equal ^=

Phép toán điểm chấm động (floating point) trên các biến

BASH không hỗ trợ cho các tính toán điểm chấm động (floating point). Tuy nhiên, lệnh bc có thể giúp tiến hành các loại toán này. Cú phép đơn giản để tiến hành là:

 [TÊN_BIẾN]=$(echo "[Tham_Số]"; [Biểu_Thức_Số_Học]" | bc [[Tham_Số_Ngắn]])

Giá trị của [Tham_Số] Có thể là:  

  • length số chữ số có nghiã trong biểu thức
  • scale số chữ số thập phân sau dấu chấm trong biểu thức
  • sqrt hay read.

Giá trị của [Tham_Số_Ngắn] Có thể là:

  • -l cho thư viện toán,
  • Các tham số khác như là  -i,-q,-s. (xem thêm chi tiết bằng lệnh hướn dẫn  man bc)

Khi dùng Tham số ngắn -l thì mệnh lệnh sẽ load từ trước thư viện toán bao gồm cả các hàm s(x) --sine, c(x) -- cosine, a(x) -- arctan, l(x) logarith tự nhiên, e(x) -- mũ cơ số e, và j(n,x) -- Hàm Bessel của số nguyên n của x.

Lưu ý: lệnh bc hỗ trợ nhiều tính toán có khả năng lập trình được với nhiều tính năng khác bao gồm cả biến số, vòng lặp và phân nhánh. Xem thêm chi tiết bằng lệnh  man bc

Thí dụ1:

#this example calculates Pi number with 10 digit of accuracy
Pi = $(echo "scale=10; 4*a(1)" | bc -l)   # using arctan(1) to calculate Pi/4
echo "PI=$Pi"  #it should be 3.1415926532

Thí dụ2:

# this example call bc without the short_option -l
radius=1.234
Pi=3.1416
$result=$(echo "scale=4; $radius*$radius*$pi" |bc)  # we need only 4 digits
echo "the area occupied by this circle is :$result"

Dùng expr để thực thi các phép toán

Đây là lệnh đánh giá một mệnh đề cuả BASH. Nó trả về giá trị trong các phép toán số học (trả về giá trị nguyên), các so sánh (trả về 1 hay 0), các phép toán lên chuỗi kí tự. Ta quy ước gọi ARG, ARG1 và ARG2 là các tham số trong biểu thức cần được đánh giá. (tham số này sẽ là giá trị của một biến (nếu biến đó là myvar thì ARG phải viết thành $myvar), của một string (các string phải để trong ngoặc " hay ' ) , hay của một hằng

Lưu ý : Lệnh expr có thể không hoạt động hữu hiệu trong một số cài đặt mặc định cho X Windows (chẳng hạn như chương trình "Terminal Emulation - trình đơn Shell Console của SUSE SLES 9. Để chạy được lệnh expr phải dùng trình đơn Linux Console trong X Window). Muốn cho lệnh này hoạt động đúng chức năng, tốt nhất là chọn chế độ chạy trên đầu cuối trực tiếp

Phép toán số học

Dùng cú pháp

expr ARG1 [Phép_Toán] ARG2  

Phép so sánh

Trả về 1 nếu mệnh đề đúng và 0 nếu sai.  ARG1 và ARG2 của nó có thể là giá trị số nếu là phép so sánh hai số hay giá trị chuỗi kí tự nếu là phép so sánh hai string. Các phép toán bao gồm

  • =  Kiểm nghiệm sự bằng nhau của hai hạng tử
  • \> Kiểm nghiệm sự lớn hơn của hạng tử bên trái
  • \<  Kiểm nghiệm sự nhỏ hơn của hạng tử bên trái
  • \>= Kiểm nghiệm sự lớn hơn hoặc bằng hơn của hạng tử bên trái
  • \<= Kiểm nghiệm sự nhỏ hơn hoặc bằng hơn của hạng tử bên trái

Cú pháp chung là

 expr ARG2 [Phép_So_Sánh] ARG1

Phép toán lên chuỗi kí tự

Ngoài phép so sánh và phép tính số học, expr cung cấp thêm một số phép toán trên các string: ARG lúc này phải có giá trị của một string (đặt chúng trong ngoặc ' hay " ). Để thực hiện thì đòi hỏi dùng thêm các từ khoá như match, substr, index, và length

expr substr ARG [Vi_Trí] [Độ_Dài]

trả về một chuỗi kí tự con của ARG bắt đầu từ vị trí [Vi_Trí] và có độ dài là [Độ_Dài]. Nếu [Độ_Dài] lớn hơn độ dài còn lại của ARG thì nó chỉ trả về phần còn lại của ARG

expr [index] ARG [string] 

Trả về chỉ số (hay vị trí) của string trong ARG nếu nó được tìm thấy

expr length ARG

Trả về độ dài của string ARG

Lưu ý: Ngoài ra, expr còn cung cấp một số phép toán khác như là phép logic hoặc  |, logic và &, và phép tương hợp với một dạng thức expr match ARG RE ; trong đó, RE là một biểu thức chính quy (một cú pháp tương đương là expr ARG:RE ). Sẽ trả về 0 nếu  ARG không tương hợp với RE; ngược lại sẽ là giá trị khác 0 thường là độ dài của RE. Tuy nhiên, trong nhiều trường hợp lệnh  expr này không tương thích được hoàn toàn với hệ điều hành và trở nên khó dùng nên tránh và có thể thay bằng các lệnh khác chẳng hạn như dùng các phần đa trình bài trong bài 2 phần 6, 7, và 8

Thí dụ

  • phép modulo
expr 8 % 3
# 2
  • Phép cộng
y=5
y=`expr $y + 1` 
echo "\$y=$y"
# 6
x=10
sum=`expr $x + $y`
echo "\$sum=$sum"
# 16
  • Phép so sánh logic
x=2
y=4
res=`expr $x = $y`
echo "evaluation of expression = retunrs $res"
# 0

res=`expr $x \< $y` echo "evaluation expression < ruturn $res" # 1
  • Phép toán trên các string
mystr="abcd 12345abcddd"
<BER> #so sánh str="2345" if [ `expr $mystr = $str` = 0 ]; then echo "comparing of $mystring and $str returns false (0)" else echo "comparing of $mystring and $str returns true (1)" fi

#trả về chuỗi kí tự con res=`expr substr "$mystr" 3 4` echo "string at position 3 with the length of 4 from $mystr is: $res" # "cd 1"

#trả về chỉ số của chuỗi kí tự con nếu tìm thấy res=`expr index $mystr $str` # 5

Dùng cơ số trên các biến số

Trong mặc định thì BASH thông dịch các giá trị số trong cơ số thập phân. Có thể dùng lệnh let để sử dụng giá trị một số cơ số không thập phân. Thí dụ sau đây sẽ chỉ ra cách tiến hành:

Thí dụ:

#Decimal 
let    "d=12" 
echo "value of d is $d" #it is 12

#Octal let "o = 012" #using number zero (0) as prefix for octal echo "o =$o" # it is now 12 (octal)=> 10<BR
#Hexadecimal let "h = Ox1c" #using '0x' as prefix for hexadecimal echo "h = $h" # it is now 1c (hex) => 28

#other base between 2 and 64 let "b = 32#15 #format as BASE#VALUE echo "base 32 predefined value of b is $b" #result 40

Tham chiếu gián tiếp tới biến

Trong thực tiễn, đôi khi người lập trình muốn truy đọc gía trị của một biến mà tên của biến đó lại là nội dung giá trị của một biến khác. Tức là nếu myVar được mang giá trị "myIndirectVar", và nếu lại có một biến mang tên myIndirectVar chứa giá trị là "XYZ", thì người ta có thể hoặc là dùng lệnh eval myNewVar=\$$myVar hay myNewVar=${!myVar} để gán giá trị "XYZ" cho biến myNewVar

eval [TÊN_BIẾN]=\$$[TÊN_BIẾN_THAM_CHIẾU]

Lệnh này không cho phép xuất (output) trực tiếp giá trị thành một chuỗi kí tự nhưng cho phép gán giá trị (chuỗi kí tự) đó lên một biến khác

Thí dụ:

myVar="Indirect_Var"
#MyVar is assigned a value as "Indirect_Var"
Indirect_Var="Here is the 1st value" 
#The Indirect_Var has bên declared and contained the value of "Here is the 1st value"

eval MSG=\$$myVar echo "here is: $MSG"

Indirect_Var="Here is the 2nd value"

#reassign a new value for Indirect_Var, #but it cannot be directly output #and must use eval command to get it

eval MSG=\$$myVar echo "here is after changing: $MSG"

[TÊN_BIẾN]=${![TÊN_BIẾN_THAM_CHIẾU]}

Lệnh này cũng làm cùng một thao tác nhưng có thể cho phép hiển thị trực tiếp giá trị chuỗi kí tự mà nó tham chiếu gián tiếp tới

Thí dụ:

myVar=An_Indirect_Var                   
#MyVar is assigned a value as "An_Indirect_Var"
An_Indirect_Var="Here is the 1st value" 
#An_Indirect_Var has a value as "Here is the 1st value"

echo "here is: ${!myVar}"

An_Indirect_Var="Here is the 2nd value" #you cannot directly output echo "here is after changing: ${!myVar}"

Các câu lệnh phân nhánh

Câu lệnh If

Như mọi ngôn ngữ lập trình, biểu thức if có thể dùng để tạo sự rẽ nhánh. Lưu ý rằng trong BASH mỗi mệnh đề if luôn luôn phải có kết thúc bằng từ khoá fi

Câu lệnh if đơn giản

Dùng cú pháp sau đây

if [ [Biểu thức_Điều_Kiện] ]; then 
       [Khối_Lệnh]
fi

Hay là :

if [ [Biểu thức_Điều_Kiện] ] 
then 
         [Khối_Lệnh]
fi 

Hay là :

if [ [Biểu thức_Điều_Kiện] ]; then [Khối_Lệnh]; fi 

Hay là bất kì dạng tổ hợp nào khi thay thế mỗi kí tự đầu dòng bằng một kí tự ;

Lưu ý:

Như một sự nhắc nhớ: các khoảng trống (space) giữa dấu ngoặc vuông và biểu thức điều kiện cần phải được giữ đúng nếu không muốn bị trình dịch bắt lỗi cú pháp

Câu lệnh phức hợp

if [ [Biểu thức_Điều_Kiện1] ];then 
    [Khối_Lệnh1]  
elif [ [Biểu thức_Điều_Kiện2] ] 
    [Khối_Lệnh2] 
elif [ [Biểu thức_Điều_Kiện3] ] 
    [Khối_Lệnh3] 
else    
    [Khối_Lệnh4]
fi 

Từ khoá elif có thể dùng để "kéo dài" số trường hợp phân nhánh ra hoặc rút ngắn lại thành câu lệnh if-else có cú pháp như sau

if [ [Biểu thức_Điều_Kiện] ];then 
    [Khối_Lệnh1]  
else 
    [Khối_Lệnh2] 
fi 

Lưu ý: Như đã biết, thay vì viết từ khoá then trong một hàng mới, bạn luôn luôn có thể thay kí tự xuống hàng bằng ; thành ;then và giữ từ khoá này trong cùng một hàng. Tương tự cho từ khoá "do" trong các vòng lặp.  

Thí dụ:

if  [ $?=0 ]; then echo "no error found"; else echo "found error: $?"; fi.

Và điều này đúng cho bất kì tổ hợp câu lệnh nào trong BASH

'Thí dụ: Đoạn mã sau đây tiến hành lệnh cp (chép tập tin file1 sang thành file2) và sau đó kiểm tra xem kết quả của lệnh cp (có trả về lỗi Khác không nào hay không). Nếu có lỗi tiến hành lệnh cp, thì kiểm xem tồn tại LOG file thì chép nối vào log file. Còn không có LOG file thì chỉ hiển thị ra màn hình

LOG="mylog.txt"
....
cp file1 file2
if [ $? = 0 ]; then
   echo "suceed!"
elif [ -e "$LOG" ]; then
   echo "failed" >> "$LOG"
else
   echo "copy failed"
fi

Phân nhánh dùng lệnh case

Tương tự, câu lệnh case cho phép phân làm nhiều nhánh cùng lúc thay vì phải dùng tổ hợp if-else

case [TÊN_BIẾN] in
    [Giá_Trị1])
        [Khối_Lệnh_1]
        ;;
    [Giá_Trị2])
        [Khối_Lệnh_1] 
        ;;
    ...
    *)    #default
        [Khối_Lệnh_Mặc_Định]
        ;;
esac

Thí dụ:

#this program can run only you have X-window; 
#the example indicated that it is compatible with wild card characters.
echo -n "chose font color for xterm in x-window: "
read color
case "$color" in
[Bb]l??)
    xterm -fg blue &
    ;;
gree*)
   xterm -fg darkgreen &
   ;;
red | orange)                  #red or orange
   xterm -fg "$color" &
*)
   xterm -fn terminal &
   ;;
esac

Lưu ý: trường hợp không muốn xác định giá trị cụ thể của [TÊN_BIẾN] mà vẩn muốn thi hành tiếp các lệnh điều khiển thì có thề dùng trường hợp *) như là sự mặc định giá trị của biến không thoả mãn bất kì trường hợp thử nào thì sẽ thi hành [Khối_Lệnh_Mặc_Định]  đó. Tuy nhiên, cũng không nhất thiết phải có sử dụng trường hơp *) này trong mỗi câu lệnh case.

Các câu lệnh vòng lặp

Khi có một khối lệnh cần được thực thi nhiều lần, mỗi lần chỉ khác nhau một vài giá trị gán ban đầu của các biến thì người ta có thể dùng kĩ thuật vòng lặp. Những biến có chỉ thị để thay đổi giá trị cài đặt ban đầu cho mỗi lần thực thi khối lệnh của vòng lặp gọi là biến chỉ số (index variable). Tập họp các giá trị được lần lược gán lên biến chỉ số X theo thứ tự được gọi là danh sách giá trị của biến X (hay ngắn gọn hơn là [Danh_Sách] )

Bash hỗ trợ 4 loại câu lệnh cho vòng lặp là for, while, until , và select .

Trước khi vào đề thì một trong những lưu ý quan trọng nhưng rất cổ điển là khi dùng vòng lặp phải nhớ hai điều:

Nó phải có lối thoát nếu không muốn tạo ra tình trạng treo chương trình , treo máy.

Các điều kiện cực biên phải chính xác. Nếu điều kiện biên sai sót sẽ dẫn đến tình trạng chương trình có hỏng hóc khó sửa vì sự sai sót chỉ xãy ra ỏ các giá trị cực biên và thông thường chương trình tưởng chừng chạy hoàn hảo cho tới khi .... một trong các biến của vòng lặp đạt giá trị cực biên.

Bên cạnh đó, còn có các lệnh chuyên dùng trong các vòng lặp để điều chỉnh hướng thực thi

Điều chỉnh vòng lặp: breakcontinue

Nhiều mệnh lệnh như là break, continue,  exit, exec, ... có thể làm đổi hướng hay ngưng lập tức sự vận hành của các vòng lặp. Trong phần này chúng ta chỉ giới thiệu sơ lược. Phần chi tiết sẽ bàn tới trong 13.7. Các thí dụ về chúng cũng sẽ được trình bày trong phần kế tiếp

break

Lệnh này sẽ chấm dứt vòng lặp nhỏ nhất chứa nó ngưng ngay lập tức và tiếp tục chạy các mã tiếp theo

continue

Lệnh này sẽ buộc ngưng thi hành phần còn lại của mã và bắt đầu vòng chu kì mới tiếp theo của vòng lặp

Ngoài ra

Vòng lặp còn có thể ngưng một khi lệnh exit, exec (hay các lệnh có tính kết thúc tiến trình hiện tại để thi hành việc khác hay để trả về hệ điều hành)

Cách thành lập [Danh_Sách] cho một vòng lặp

Như trong vòng lặp for của C/C++, trong 1 vòng lặp thì danh sách có thể là các số nguyên tăng (hay giảm) một hằng số nguyên và cách thành lập này hoàn toàn tương tự. Bạn có thể xem lại cú pháp cho lệnh for.

Ngoài ra, BASH còn hỗ trơ các hình thức tạo lập danh sách khác dùng trong lệnh for và lệnh select như sau

Dùng mặc định

Theo mặc định thì một [Danh_Sách] dùng trong vòng lặp for có thể là một trong các dạng

Các tên chuỗi kí tự phân cách bởi khoảng trống (space)

Thí dụ1:

for FRUIT in  'apple' 'apricot' 'lemon' 'plum' 'orange'; do
     if [ "$FRUIT" = "lemon" ]; then
         echo "lemon found.  Using 'continue' key word to jump over this record" 
         continue
     if
     echo "The fruit is $FRUIT"
done

Danh sách các số phân cách bởi khoảng trống (space)

Thí dụ2:

for i in 1 2 3 4; do
     echo "$i"
done

Danh sách các tên như thí dụ 1,2,3 nhưng phân cách bởi kí tự xuống hàng

Thí dụ3:

LIST="/root/file1
/etc/file2
/var/spool/file3"
for FILE in $LIST; do
     if [ -e $FILE ]; then
          echo "file $FILE exist"
     else
          echo "file $FILE not exist. Stop any loop by 'break' command"
          break
    fi  
done

Danh sách có thể cho được từ các tên dùng kí tự phỏng định

Thí dụ4:

for FILE in [abc]*; do
     rm -f $FILE
done
#this script will remove all files whose name beginning by character a, b, or c in current directory

Danh sách có thể cho được từ các dòng hiển thị của một khối lệnh (hay một mệnh lệnh)

Thí dụ5

for file in $( find $directory -type l )   # -type l = symbolic links
do
  echo "$file"
done | sort 
#this for loop will search all soft link file and output after sort them 

Ngăn cách giữa các miền cho một [Danh_Sách]

Thật ra, danh sách các tên tạo ra mà chúng ngăn cách nhau một cách mặc định đã được quy định trước bởi giá trị của biến ngăn cách (separator) IFS trong BASH. Đó là các giá trị kí tự khoảng trống, kí tự nhảy bước, hay kí tự xuống hàng (space,tab,newline). Chúng ta hoàn toàn có thể cài đặt lại giá trị này (chứa trong $IFS ) để sử dụng và trả lại giá trị ban đầu sau khi dùng xong. Thí dụ sau đây minh hoạ cho thao tác này:

Thí dụ:

# file runnit
#!/bin/bash
fruits=plum:orange:grape:banana  
#the 'fruit' list using ':' as separate character
old="$IFS"   #save the current shell separator
IFS=":"      #reassign the new ':' separator
for FRUIT in $fruits
do 
     echo "this is $FRUIT
done
IFS="$old"   #returning original value

for

Biến chỉ số [TÊN_BIẾN] sẽ lần lượt lấy các giá trị trong [Danh_Sách] và thi hành theo thứ tự trừ khi bị ngắt ngang bởi các lệnh điều chỉnh vòng lặp như break, continue , hay exit, exec. Sau đây là một số cú pháp tiêu biểu

for [TÊN_BIẾN] in [Danh_Sách]; do
       [KHỐI_LỆNH]
done
for [TÊN_BIẾN] in [Danh_Sách]
do
       [KHỐI_LỆNH]
done
for ((a=MIN; a <= MAX; a++))
do
      [KHỐI_LỆNH]
done
for ((a=MIN1; b=MIN2;  a<MAX; a++; b++))
do
      [KHỐI_LỆNH]
done
for (( [Biểu_Thức1]; [Biểu_Thức2]; [Biểu_Thức3]; )) ; do  [KHỐI_LỆNH] ; done

Trong đó, [Biểu_Thức1] sẽ được đánh giá sau đó là việc đánh giá của biểu [Biểu_Thức2] và mỗi lần [Biểu_Thức2] có giá trị khác 0, thì [KHỐI_LỆNH] sẽ được thi hành và sau đó sẽ là việc đánh giá [Biểu_Thức3]. Nếu bất kì biểu thức nào không có mặt thì chỗ thiếu vắng đó sẽ được xem như là một biểu thức luôn luôn có giá trị 1. Giá trị trả về của vòng lặp này là trạng thái thoát (exit status) của mệnh lệnh sau cùng trong khối lệnh, hay trả về giá trị "false" nếu các biểu thức là không hợp lệ.

Như là một bài tập, hãy thử nghiệm các vòng lặp for với biến chỉ số giảm dần thay vì tăng như trong cú pháp nêu trên; đồng thời hãy thử dùng giá trị cực tiểu (MIN) ỏ trên là số âm để xem khả năng hỗ trợ của BASH.

Lưu ý: Việc trình bày bất kì một khối lệnh hay câu lệnh nào trong BASH đều có thể thay các kí tự xuống hàng bởi các dấu ;

Thí dụ1:

for fruit in "red apple" "yellow orange"   "green grape"
do
     echo "color fruit :  $fruit"
done

Thí dụ2:

for CHAR in `cat $myFile.txt`; do
     echo  "$CHAR"
done

Xem thêm thí dụ về vòng lặp for trong phần nói về danh sách

 while

Biểu thức  [Biểu thức_Điều_Kiện] sẽ được kiểm nghiệm cho tới khi nó không còn được thoả mãn thì vòng lặp sẽ bị ngừng . Các cú pháp tương đương hay thấy là:

while [ [Biểu thức_Điều_Kiện] ]; do
      [KHỐI_LỆNH]
done
while [ [Biểu thức_Điều_Kiện] ] 
do
      [KHỐI_LỆNH]
done
while [ [Biểu thức_Điều_Kiện] ]; do [KHỐI_LỆNH]; done

Thí dụ1:

var=0
MAX=10

while [ "$var" -lt "$MAX" ] do echo -n "$var " # -n suppresses new-line. var=$(($var+1)) done #revise the above while statement using 'break' keyword var=0 MAX=10 while (true); do echo -n "$var " # -n suppresses new line. var=$(($var+1)) if [ $var -eq 10 ]; then break fi done

Thí dụ2:

#Example of multiple condition line of while statement
#!/bin/bash
var=init
pre=$var 

while echo "previous variable = $pre" echo pre=$var [ "$var" != "q" ] # Four conditions on "while", but only the last one controls loop. do echo -n "Input variable (enter 'q' to exit) :" read var echo "variable = $var" done

Thí dụ3:

#example while using C style
#BASH STYLE:"
LIMIT=10
a=1
while [ "$a" -le $LIMIT ]
do
  echo -n "$a "
  let "a+=1"
done           # No surprises, so far.
echo; echo

# Now, with C style. ((a = 1)) # a=1 # Double parentheses permit space when setting a variable, as in C. while (( a <= LIMIT )) # Double parentheses, and no "$" preceding variables. do echo -n "$a " ((a += 1)) # let "a+=1" # Double parentheses permit incrementing a variable done

until

tương tự như vòng lặp while nhưng [Biểu thức_Điều_Kiện] được kiểm nghiệm trước cho tới khi giá trị của nó là "true" thì vòng lặp sẽ bị ngừng

until [ [Biểu thức_Điều_Kiện] ]; do
     [KHỐI_LỆNH]
done

until [ [Biểu thức_Điều_Kiện] ]
do
    [KHỐI_LỆNH]
done
until [ [Biểu thức_Điều_Kiện] ]; do [KHỐI_LỆNH]; done

Thí dụ:

#!/bin/bash
var=
until [ "$var" = "q" ] # Tests condition here, at top of loop.
do
     echo -n "Input variable (enter 'q' to exit :"
     read var
     echo "variable = $var"
done  

select

Đây là dạng vòng lặp mô phỏng của trình bao Korn. Nó có ích để tạo một trình đơn (menu) lựa chọn từ một danh sách của tên mà biết chỉ số sẽ lần lược được gán.

PS3="[Dòng_Thông_Báo]"  # it will be prompted in the 'select' statement
...
select [TÊN_BIẾN] in [ [Danh_Sách] ]
do
     [KHỐI_LỆNH]
     break;
done

Thí dụ:

#!/bin/bash
PS3='Choose your favorite fruit: ' # Sets the prompt string.
#this is default variable of 'select' statement
echo
select FRUIT in "orange" "grape" "banana" "plum" "mango"
do
      echo "Your fruit is $FRUIT."
      echo "Yummy!"
      echo
      break  # without  'break'  the loop will be infinity
done

Vòng lặp lồng nhau (net loop) và điều chỉnh vòng lặp

Để tránh lỗi mập mờ (ambiguity), lệnh break và continue trong các vòng lặp lồng nhau có hỗ trợ về cấp độ của vòng lặp chịu ảnh hưởng bởi sự thi hành của nó.

  • break [n] : Với [n] là cấp độ của vòng lặp sẽ bị điều chỉnh bởi lệnh break . Trong đó, break 1 (hay viết đơn giản break ) sẽ chỉ ảnh hưởng đến vòng lặp trong cùng nhất có chứa từ khoá break . thí dụ break 2 sẽ chỉ có ảnh hưởng tới vòng lặp kế bao ngoài vòng lặp trong cùng (cấp 2)
  • continue [n] : Hoàn toàn tương tự continue n sẽ có hiệu lực đối với vòng lặn cấp n

Thí dụ:

#!/bin/bash
#name nestdemo
for month in jan feb mar apr may jun jul aug sep oct nov dec; do
    for week in 1 2 3 4; do  
        echo -n "Start processing the month of $month. (y/n)? :"
        read ans
        if [ "$ans" = "n" ] || [ -z "$ans" ]; then
             continue 2  
             #skip the rest go back to next value of $month
             # from the outer loop
        else
             echo -n "Start processing week $week of the month $month (y/n)?"
             read ans
             if [ "$ans" = "n" ] || [ -z "$ans" ]; then
                  continue      
                  #skip the this inner most loop 
             else
                  echo "Now processing week $week og the month $month"
                  #do  some commands
                  echo "Done"
             fi
       fi
    done
done



Trở về mục lục

Đọc bài kế

Liên kết đến đây