Các Lược Giảng Chuyên Sâu về Sử Dụng Văn Lệnh BASH trong Linux/Bài 3
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
-
1
Phép
toán
dùng
với
các
biểu
thức
BASH
-
1.1
Phép
toán
thử
nghiệm
tập
tin
(file
test)
trong
các
biểu
thức
-
1.1.1
Phép
toán
lên
một
hạng
tử
- 1.1.1.1 [ -b [Tên_Tập_Tin] ]
- 1.1.1.2 [ -c [Tên_Tập_Tin] ]
- 1.1.1.3 [ -d [Tên_Tập_Tin] ]
- 1.1.1.4 [ -e [Tên_Tập_Tin] ]
- 1.1.1.5 [ -f [Tên_Tập_Tin] ]
- 1.1.1.6 [ -g [Tên_Tập_Tin] ]
- 1.1.1.7 [ -k [Tên_Tập_Tin] ]
- 1.1.1.8 [ -O [Tên_Tập_Tin] ]
- 1.1.1.9 [ -P [Tên_Tập_Tin] ]
- 1.1.1.10 [ -L [Tên_Tập_Tin] ]
- 1.1.1.11 [ -S [Tên_Tập_Tin] ]
- 1.1.1.12 [ -r [Tên_Tập_Tin] ]
- 1.1.1.13 [ -s [Tên_Tập_Tin] ]
- 1.1.1.14 [ -u [Tên_Tập_Tin] ]
- 1.1.1.15 [ -w [Tên_Tập_Tin] ]
- 1.1.1.16 [ -x [Tên_Tập_Tin] ]
- 1.1.2 Phép toán lên hai hạng tử
-
1.1.1
Phép
toán
lên
một
hạng
tử
- 1.2 Phép toán so sánh trên các số nguyên
- 1.3 Phép toán so sánh trên các chuỗi kí tự
- 1.4 Phép toán logic trên các mệnh đề con
- 1.5 Phép toán logic trên các biểu thức
-
1.1
Phép
toán
thử
nghiệm
tập
tin
(file
test)
trong
các
biểu
thức
-
2
Phép
toán
trên
các
biến
- 2.1 Phép toán lên chuỗi kí tự
- 2.2 Phép toán số học trên biến nguyên
-
2.3
Phép
toán
nhị
phân
(Binary
oparation)
trên
các
biến
nguyên
- 2.3.1 shift left <<
- 2.3.2 shift left-equal <<=[n]
- 2.3.3 Phép toán nhị phân shift right >>
- 2.3.4 Phép toán nhị phân shift right-equal >>=[n]
- 2.3.5 Phép toán nhị phân AND &
- 2.3.6 Phép toán nhị phân AND-equal &=
- 2.3.7 Phép toán nhị phân OR |
- 2.3.8 Phép toán nhị phân OR-equal |=
- 2.3.9 Phép toán nhị phân nghịch đảo ~
- 2.3.10 Phép toán nhị phân NOT !
- 2.3.11 Phép toán nhị phân XOR ^
- 2.3.12 Phép toán nhị phân XOR-equal ^=
- 2.4 Phép toán điểm chấm động (floating point) trên các biến
- 2.5 Dùng expr để thực thi các phép toán
- 2.6 Dùng cơ số trên các biến số
- 3 Tham chiếu gián tiếp tới biến
- 4 Các câu lệnh phân nhánh
-
5
Các
câu
lệnh
vòng
lặp
- 5.1 Điều chỉnh vòng lặp: break và continue
-
5.2
Cách
thành
lập
[Danh_Sách]
cho
một
vòng
lặp
-
5.2.1
Dùng
mặc
định
- 5.2.1.1 Các tên chuỗi kí tự phân cách bởi khoảng trống (space)
- 5.2.1.2 Danh sách các số phân cách bởi khoảng trống (space)
- 5.2.1.3 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
- 5.2.1.4 Danh sách có thể cho được từ các tên dùng kí tự phỏng định
- 5.2.1.5 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)
- 5.2.2 Ngăn cách giữa các miền cho một [Danh_Sách]
-
5.2.1
Dùng
mặc
định
- 5.3 for
- 5.4 while
- 5.5 until
- 5.6 select
- 5.7 Vòng lặp lồng nhau (net loop) và điều chỉnh vòng lặp
Phép toán dùng với các biểu thức BASH[sửa]
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[sửa]
Phép toán lên một hạng tử[sửa]
[
-b [Tên_Tập_Tin]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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ử[sửa]
[
[Tên_Tập_Tin_1]
-nt
[Tên_Tập_Tin_2]
]
[sửa]
[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]
]
[sửa]
[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]
]
[sửa]
[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]
và
[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]
và
[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[sửa]
Toán
tử
-eq
[sửa]
Trả về giá trị "true" nếu cả hai hạng tử bằng nhau.
Thí dụ:
[ $a -eq 5 ]
Toán
tử
-ne
[sửa]
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
[sửa]
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
[sửa]
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
[sửa]
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ự[sửa]
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ử =[sửa]
Trả về giá trị "true" nếu cả hai hạng tử bằng nhau.
Thí dụ:
[ $a = "mystring" ]
Toán
tử
!=
[sửa]
Trả về giá trị "true" nếu cả hai hạng tử không bằng nhau.
Thí dụ:
[ $a != $b ]
Toán tử \<[sửa]
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ử \>[sửa]
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[sửa]
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[sửa]
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[sửa]
Toán tử -a[sử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[sửa]
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[sửa]
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[sửa]
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ự[sửa]
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,7 và 8
Phép toán số học trên biến nguyên[sửa]
Để á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ử =[sửa]
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 + [sửa]
Thí dụ:
let "var1 = $var2 + 2 + $var1"
Trừ
-
[sửa]
Nhân
*
[sửa]
Chia /[sửa]
Lũy
thưà
**
[sửa]
Modulo %[sửa]
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) +=[sửa]
Thí dụ:
let "var1 +=5" # it equivalent to the command: let "var1 = $var1 +5" or command ((var1 += 5))
Minus-equal
(trừ
thêm)
-=
[sửa]
Multiply-equal (nhân thêm)*=[sửa]
Divide-equal (chia thêm) /=[sửa]
Modulo-equal (mod thêm) %=[sửa]
Phép toán nhị phân (Binary oparation) trên các biến nguyên[sửa]
shift left <<[sửa]
Đẩy các bit sang trái 1 bit (tương đương với nhân cho 2)
shift
left-equal
<<=
[n]
[sửa]
[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 >>[sửa]
Đẩy sang phải 1 bit
Phép
toán
nhị
phân
shift
right-equal
>>=
[n]
[sửa]
[n]
Phép toán nhị phân AND &[sửa]
Phép toán nhị phân AND-equal &= [sửa]
Phép toán nhị phân OR |[sửa]
Phép toán nhị phân OR-equal |=[sửa]
Phép toán nhị phân nghịch đảo ~[sửa]
Phép toán nhị phân NOT ![sửa]
Phép toán nhị phân XOR ^[sửa]
Phép toán nhị phân XOR-equal ^= [sửa]
Phép toán điểm chấm động (floating point) trên các biến[sửa]
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
hayread
.
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[sửa]
Đâ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[sửa]
Dùng cú pháp
expr ARG1
[Phép_Toán]
ARG2
Phép so sánh[sửa]
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ự[sửa]
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ụ[sửa]
- 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ố[sửa]
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[sửa]
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]
[sửa]
[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]
}[sửa]
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[sửa]
Câu lệnh If[sửa]
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[sửa]
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[sửa]
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[sửa]
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[sửa]
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:
break
và
continue
[sửa]
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
[sửa]
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
[sửa]
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[sửa]
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[sửa]
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[sửa]
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)[sửa]
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)[sửa]
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[sửa]
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[sửa]
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)[sửa]
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]
[sửa]
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[sửa]
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[sửa]
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[sửa]
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[sửa]
Đâ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[sửa]
Để 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
: Với[n]
[n]
là cấp độ của vòng lặp sẽ bị điều chỉnh bởi lệnhbreak
. 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
: 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[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