Các Lược Giảng Chuyên Sâu về Sử Dụng Văn Lệnh BASH trong Linux/Bài 5
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 5: Đào sâu chi tiết: Lệnh có sẵn trong BASH, Truy sửa lỗi cho văn lệnh, và Đa luồng
Mục lục
-
1
Các
lệnh
có
sẵn
(buildin)
trong
BASH
- 1.1 : Dấu hai chấm (colon)
- 1.2 .[TÊN_TẬP_TIN] [Tham_Số]
- 1.3 eval [KHỐI_LỆNH]
- 1.4 exec [[LỆNH] [[THAM_SỐ]]]
- 1.5 exit [n]
- 1.6 export [-f][-n] [[TÊN][=[Giá_Trị]]]
- 1.7 getopts [Dãy_Tham_Số[:]]
- 1.8 hash [-r] [-dt] [[Tên_lệnh]]
- 1.9 readonly [-apf] [[Danh_Sách_Tên_Biến]]
- 1.10 test [Biểu_Thức]
- 1.11 basename [Tên_Đường_Dẫn]
- 1.12 dirname [Tên_Đường_Dẫn]
- 1.13 true và flase
- 1.14 trap [-lp] "[KHỐI_LỆNH[;]]" [Danh_Sách_Tín_Hiệu]
- 1.15 umask [-p] [-S] [[số_cơ_chế]]
- 1.16 command [Lệnh] [Tham_Số_Lệnh]
- 1.17 enable [-n] [-p] [-a] [Tên]
- 1.18 help [[TÊN]] [-s [Dạng_thức]]
- 1.19 printf [Định_Dạng] [[Các Đối_Số]]
- 1.20 read [-rs] [-a [Tên_Mảng]] [-d [Kí_tự_giới_hạn]] [-n [n]] [-t [Thời_hạn]] [-u [fd]] [[Danh_Sách_Biến]]
- 1.21 type [-afptP] [[Danh_Sách_Tên]]
- 1.22 ulimit [-acdflmnpstuvSH]limit]
- 2 Quá trình Truy sửa lỗi trong BASH
- 3 Lập trình đa luồng (multi threading) trong BASH
Các lệnh có sẵn (buildin) trong BASH[sửa]
Sau đây là các lệnh của BASH đa số thừa hưởng từ Bourne. Các lệnh theo chuẩn POSIX 1003.2. Một số đã được diễn giảng trong các bài trước. Phần này chỉ trình bày những gì quan trọng và chưa được đề cập. Những lệnh ít quan trọng hơn hay dể dùng xin xem bảng phụ lục của bài 1. Các dấu ngoặc vuông [] với màu đen trong các cú pháp lệnh sẽ có ý nghiã rằng các tham số trong dấu ngoặc là tùy chọn.
Lưu ý: Đối với các lệnh sẵn có của BASH, người dùng không thể dùng man <Tên_Lệnh> để đọc cách sử dụng các lệnh này mà thay vào đó là lệnh help
: Dấu hai chấm (colon)[sửa]
Không làm gì hết, thực thi dòng lệnh kế. Trả về giá trị 0
.[TÊN_TẬP_TIN]
[Tham_Số][sửa]
Lệnh
dấu
chấm
(period)
dùng
để
tải,
đọc,
và
thực
thi
các
lệnh
từ
trong
nội
dung
của
[TÊN_TẬP_TIN]
với
các
tham
số
là
[Tham_Số].
Gía
trị
trả
về
là
trạng
thái
thoát
của
mệnh
lệnh
cuối
cùng
được
thực
thi
trong
tập
tin.
Nếu
Tập
tin
được
tải
có
định
nghiã
các
hàm,
thì
các
hàm
này
có
thể
được
gọi
sau
đó
(Xem
lại
phần
tải
hàm
số
bài
4
)
nếu
tên
là
tên
riêng
thì
trình
dịch
sẽ
đi
tìm
tên
này
trong
các
đuờng
tìm
kiếm
chưá
trong
$PATH.
Trường
hợp
không
tìm
thấy
tên
tập
tin
này
trình
dịch
trả
về
số
khác
0.
Như
đã
nói
mệnh
lệnh
này
tương
đương
với
source
...
eval [KHỐI_LỆNH]
[sửa]
Đây
là
lệnh
đánh
giá.
Lệnh
này
trả
về
giá
trị
chính
là
giá
trị
trạng
thái
thi
hành
của
[KHỐI_LỆNH]
.
Nếu
không,
lệnh
không
có
thì
nó
trả
về
0
Thí
dụ:
Lệnh
sau
đây
sẽ
chép
nội
dung
của
tất
cả
các
dòng
của
tập
tin
source.txt
thoả
mãn
dạng
thức
của
tham
số
đưa
vào
(dược
áp
dụng
lên
lệnh
grep
)
vào
trong
tập
tin
found
#!/bin/bash #mycommand while [ ! "$1" = "" ]; do returncode=`eval grep $1 source.txt >> ./found` shift if [ ! "$returncode" = "" ]; then echo "string \"$1\" nout found >> found fi done
exec
[[LỆNH]
[[THAM_SỐ]
]][sửa]
Tương
tự
như
C/C++,
exec
sẽ
thay
thế
tiến
trình
của
mình
bằng
tiến
trình
thực
thi
[LỆNH]
.
Nếu
lệnh
không
có
mặt
thì
nó
phải
được
cung
ứng
qua
ngỏ
ống
dẫn
truyền.
exit [n]
[sửa]
Thoát
khỏi
chương
trình
(hay
hệ
vỏ
đang
dùng)
trả
về
giá
trị
[n]
cho
môi
trường
mẹ.
Nếu
<n>
không
dung
ứng
thì
trả
về
giá
trị
của
giá
trị
mà
lệnh
cuối
dùng
đã
thực
thi
liền
trước
đó.
Bảng Mã thoát thông dụng chuẩn | ||
---|---|---|
Số mã thoát | Ý nghiã | Ghi chú |
0 | Việc thực thi văn lệnh không có lỗi | Đôi khi không cần thiết để trả về các giá trị lỗi thì cũng có thể dùng giá trị 0 |
1 | Các lỗi thông dụng được bắt ở giá trị này | Các lỗi linh tinh, such as "divide by zero" |
2 | Dùng sai lệnh của hệ vỏ | Hiếm dùng, thường mặc định trả về giá trị 1 |
126 | Lệnh được gọi không thể thi hành | Quyền sử dụng có vấn đề hay lệnh là tập tin không thể thực thi (không có thuộc tính +x chẳng hạn) |
127 | "command not found" -Lệnh không tồn tại | Có thể có vấn đề trong cài đặt $PATH hay lỗi chính tả |
128 | Tham số không có hiệu lực (hay sai) | |
128+n | "fatal error"- Lỗi nghiêm trọng signal "n" |
exit
chỉ
chấp
nhận
giá
trị
trả
về
từ
0
đến
255 $? trả về tối đa 137 (128 + 9) |
Văn lệnh bị ngưng bởi tổ hợp phím Control-C |
Control-C
là
lỗi
nghiêm
trọng
signal
2, (130 = 128 + 2, xem dòng bên trên) |
|
255* | Trạng thái thoát ngoài mức | exit chỉ nhận các đối số nguyên trong khoảng từ 0 - 255 |
export
[-f][-n]
[[TÊN]
[=[Giá_Trị]
]][sửa]
Dùng
thể
xuất
một
[TÊN]
vào
trong
môi
trường
(biến
toàn
cục
hay
biến
môi
trường)
để
cho
phép
các
tiến
trình
con
truy
cập.
(Xem
lại
phần
trước
nói
về
lệnh
này)
Các tham số:
-
-f
cho biết đó là hàm; ngoài ra, nó sẽ là biến. Ngoài ra,[TÊN]
sẽ chỉ là biến -
-n
Hủy bỏ hiệu lực của việc xuất biến này
Nếu
không
có
[TÊN]
trong
lệnh
export
thì
lệnh
này
sẽ
là
lệnh
hiển
thị
danh
sách
các
[TÊN]
đã
xuất
Giá
trị
trả
về
là
0
trừ
khi
lệnh
bị
gọi
sai
tham
số,
hay
[TÊN]
không
phải
là
một
biến
có
hiệu
lực,
hay
trong
trường
hợp
dùng
-f
,
mà
[TÊN]
không
phải
là
một
hàm
getopts [Dãy_Tham_Số[:]]
[sửa]
Đây
là
lệnh
thoả
mãn
tiêu
chuẩn
POSIX.2
được
dùng
để
đọc
và
truy
nhập
các
giá
trị
của
tham
số
truyền
vào
một
văn
lệnh.
lệnh
là
1
công
cụ
tiện
lợi
để
tiến
hành
truy
nhập
các
giá
trị
của
tham
số.
Trong
cú
pháp
trên,
mỗi
tham
số
khi
gọi
sẽ
sẽ
có
dạng
chuẩn
là
-
[Tham_Số]
[Giá_Tri]
.
Trong
đó,
[Tham_Số]
là
tên
của
tham
số
và
là
một
kí
tự
chữ
hay
số
bất
kì
trong
bảng
kí
tự
ngoại
trừ
dấu
hai
chấm :
và
dấu
hỏi ?.
Mặt
khác,
trong
khai
báo
thì
thì
mỗi
tham
số
[Tham_Số]
trong
dãy
tham
số
nếu
được
viết
liền
sau
đó
bằng
dấu
hai
chấm
:
thì
tham
số
đó
sẽ
đòi
hỏi
phải
có
một
giá
trị
đi
kèm.
Thí
dụ:
nếu
[Dãy_Tham_Số[:]]
có
dạng
am:bc
thì
văn
lệnh
sẽ
chấp
nhận
các
tham
số
gọi
bên
ngoài
của
nó
là:
-a,
-b,
-c
và
-m
<giá_trị_của_m>
Người
ta
thường
truy
nhập
các
tham
số
của
văn
lệnh
thông
qua
một
vòng
lặp.
Nếu
văn
lệnh
được
gọi
mà
không
cung
ứng
tên
tham
số
có
trong
[Dãy_Tham_Số[:]]
thì
tham
số
đó
có
giá
trị
null.
Chỉ
số
của
tham
số
được
chứa
trong
$OPTIND
(được
khởi
động
tăng
từ
1)
và
giá
trị
của
tham
số
tương
ứng
chứa
trong
$OPTARG
Lưu
ý:
Tuy
nhiên,
đối
với
các
tham
số
có
khai
báo
giá
trị
đi
kèm
(như
trong
thí
dụ
trên
là
m:
thì
string
đi
kề
trong
các
tham
số
nhập
vào
sẽ
mặc
nhiên
xem
là
giá
trị
của
tham
số
m
)
Thí
dụ
sau
đây
minh
hoạ
cho
việc
truy
nhập
các
tham
số
thông
qua
lệnh
getopts
Parama= Paramb= Paramb_Value= Paramc= Paramc_Value= Param1= Param1_Value= while getopts a1:b:c name do case $name in a) Parama=1" echo "Parama flag is $Parama" ;; b) Paramb=1 Paramb_Value="$OPTARG" echo "Paramb flag is $Paramb, and its value is $Paramb_Value" ;; c) Paramc=1 ;; 1) Param1=1 Param1_Value="$OPTARG" echo "Param1 flag is $Param1, and its value is $Param1_Value" ;; *) echo "Usage: `basename $0`: [-a] [-b value] [c] [-1 value] args\n" exit 2 ;; esac done
shift $(($OPTIND - 1)) echo "Remaining arguments are: $*"
hash
[-r]
[-dt]
[
[Tên_lệnh]
]
[sửa]
Ghi nhớ tên đầy đủ của một lệnh để sau này trình dịch sẽ tìm ra mà không cần qua biến môi trường $PATH. Các tham số thông dụng:
-
-r
lệnh cho trình dịch quên đi mọi giá trị đã ghi nhớ -
-d
Yêu cầu trình dịch chỉ quên tên của[Tên_lệnh]
-
-t
Hiển thị lại tên đầy đủ của[Tên_lệnh]
đã ghi nhớ
readonly
[-apf]
[
[Danh_Sách_Tên_Biến]
]
[sửa]
Cài
các
tên
trong
danh
sách
đặt
tính
chỉ
đọc
được.
Cách
thức
này
có
thể
dùng
để
định
nghiã
các
hằng.
Các
tên
trong
danh
sách
có
thể
phân
cách
bởi
kí
tự
space.
Lệnh
trả
về
0
trừ
khi
một
tên
biến
là
không
hợp
lệ
hay
trong
trường
hợp
dùng
tham
số
-f
mà
tên
hàm
không
tồn
tại
Thí dụ:
a=1 b=2 readonly a b
Các tham số thông dụng:
-
-f
Khai báo biến hằng là một hàm -
-a
Khai báo biến hằng là một mảng -
-p
hiển thị các biến hằng đã khai báo dùng lệnh readonly - Không có tham số : Khai báo tên biến là hằng. Nội dung của nó không thay đổi được
test [Biểu_Thức]
[sửa]
Đánh
giá
[Biểu_Thức]
.
Lệnh
này
hoàn
toàn
tương
đương
với
biểu
thức
[
[Biểu_Thức]
]
thường
dùng
trong
các
câu
lệnh
phân
nhánh
và
vòng
lặp,
các
phép
toán
(Xem
lại
bài
3).
Các
Biểu
thức
trong
BASH
có
thể
kết
hợp
nhau
qua
các
phép
toán
để
tạo
nên
biểu
thức
mới.
Danh
sách
dưới
đây
giảm
đần
thứ
tự
ưu
tiên
của
các
phép
toán:
-
!
[Biểu_Thức]
: Trả vềtrue
nếu[Biểu_Thức]
có giá trịfalse
-
(
[Biểu_Thức]
)
: Trả về giá trị của[Biểu_Thức]
-
[Biểu_Thức1]
-a
[Biểu_Thức2]
: Trả vềtrue
nếu cả hai[Biểu_Thức1]
và[Biểu_Thức2]
đều có giá trịtrue
-
[Biểu_Thức1]
-o
[Biểu_Thức2]
: Trả vềtrue
nếu hoặc[Biểu_Thức1]
làtrue
hoặc[Biểu_Thức1]
làtrue
Thí
dụ:
lệnh
sau
đây
sẽ
kiểm
tra
nếu
tập
tin
./myfile
không
tồn
tại
thì
sẽ
thi
hành
lệnh
echo
"Error:
./myfile
not
found"
và
ngưng
chương
trình
trả
về
giá
trị
1
test -e /myfile. || (echo "Error: ./myfile not found"; exit 1)
Thí dụ2: dùng tương đương với [ ]
X=0 Y=1 if test x -gt y; then echo "$X greater than $Y" else echo "$X not greater than $Y" if
basename [Tên_Đường_Dẫn]
[sửa]
Trả về tên cuối cùng trong một đường dẫn với nhiều thư mục
Thí
dụ:
Lệnh
basename
/etc/init.d/boot
sẽ
trả
về
giá
trị
boot
dirname [Tên_Đường_Dẫn]
[sửa]
Trả
về
phần
tên
của
[Tên_Đường_Dẫn]
đã
loại
trừ
giá
trị
cuối
cùng
Thí
dụ:
Lệnh
dirname
/etc/init.d/boot
sẽ
trả
về
giá
trị
/etc/init.d
true
và
flase
[sửa]
Lệnh
true
trả
về
trạng
thái
thoát
thành
công
và
không
làm
gì
khác
cả.
Ngược
lại
false
trả
về
trạng
thái
thoát
thất
bại
cũng
không
có
hành
động
gì
khác.
Thường
được
dùng
trong
các
vòng
lặp
Thí dụ:
cnt=0 while true ; do let "cnt +=1" echo "loop counter: $cnt" if [ $cnt -gt 9 ]; then echo "out of range" break fi done
trap
[-lp]
"
[KHỐI_LỆNH[;]]
"
[Danh_Sách_Tín_Hiệu]
[sửa]
[KHỐI_LỆNH[;]]
sẽ
đuợc
thực
thi
khi
văn
lệnh
nhận
được
các
tín
hiệu
(signal)
ghi
trong
lệnh
trap
.
Đa
số
các
tín
hiệu
bị
bẫy
(trap)
sẽ
không
còn
hiệu
lực
(tức
là
bị
ngăn
chận)
như
thông
thường
đối
với
các
chương
trình
dùng
lệnh
này
mà
chỉ
có
[KHỐI_LỆNH[;]]
đưọc
thi
hành.
[Danh_Sách_Tín_Hiệu]
có
thể
cung
cấp
bằng
các
tên
ngắn,
tên
đầy
đủ,
hay
bằng
các
giá
trị
số
của
tín
hiệu
ngăn
cách
nhau
bởi
một
khoảng
trống
(space
character).
Tên
ngắn
là
tên
đầy
đủ
nhưng
bỏ
đi
tiền
tố
SIG
.
Lệnh
này
có
nhiều
ứng
dụng
như
là
sử
dụng
trap
nể
"cấm"
hay
ngăn
ngưà
các
thao
tác
hay
tác
nhân
bên
ngoài
(do
người
dùng
hay
chưong
trình
khác)
có
ý
định
làm
ngưng
trệ
các
xử
lí
tối
quan
trọng
đang
xãy
ra
trong
lúc
vận
hành.
Ngoài
ra
người
ta
hay
dùng
trap
trong
quá
trình
tìm
sửa
lỗi.
-
-p
Nếu tham số này có mặt trình bao sẽ hiển thị các lệnh trap tương ứng với các tín hiệu -
-l
Yêu cầu trình bao hiển thị danh sách các tên đầy đủ của tín hiệu và giá trị tương ứng của chúng.
Các cú pháp:
-
trap ""
[Danh_Sách_Tín_Hiệu]
: Bỏ qua các tín hiẹu trong danh dách -
trap "
[KHỐI_LỆNH]
"
[Danh_Sách_Tín_Hiệu]
: Thi hành[KHỐI_LỆNH[;]]
nếu tín hiệu bị tìm thấy -
trap
[Danh_Sách_Tín_Hiệu]
: Trả lại giá trị ban đầu cho các tín hiệu trong[Danh_Sách_Tín_Hiệu]
-
trap : [Danh_Sách_Tín_Hiệu]
: bỏ qua tín hiệu và chuyển nó vào các tiến trình con
Lưu
ý:
dấu
ngoặc
kép
có
thể
thay
thế
bằng
dấu
sắc
'
trong
việc
đặt
[KHỐI_LỆNH]
vào
kiểu
string
-
Các Tín hiệu Quan trọng Tên ngắn/Tên đủ
của Tín hiệuGiá trị
bằng sốÝ nghiã EXIT /SIGEXIT 0 Dùng để bắt các tín hiệu thoát khỏi script HUB / SIGHUP 1 logout, ngừng gọi (hang up) INT / SIGINT 2 Ngắt (interrupt) chương trình (bằng <Ctrl>+<c>) QUIT / SIGQUIT 3 Thoát khỏi lệnh đang thi hành (bằng <Ctrl>+<\> SIGILL 4 Chỉ thị không hợp lệ (illegal instruction) SIGBUS 7 Lỗi BUS SIGFPE 8 Ngoại lệ Điểm chấm động (Floating point exception) SIGKILL 9 Hủy bỏ (kill), không thể ngăn chận hay bỏ qua tín hiệu này được. SIGUSR1 10 Tín hiệu người dùng tự định nghiã 1 SIGSEGV 11 Vi phạm phân đọan (segmentation violation) SIGUSR2 12 Tín hiệu người dùng tự định nghiã 2 SIGPIPE 13 Ống dẫn truyền bị gãy TERM / SIGTERM 15 Kết thúc (terminate) - kết thúc mặc định SIGSTKFLT 16 Lỗi chồng xếp (stack fault) SIGCLD / SIGCHLD 17 Trạng thái tiến trình con thay đổi SIGCONT 18 Tiếp tục SIGSTOP 19 Stop, không thể ngăn chận tín hiệu này được SIGTSTP 20 Ngưng bàn phím SIGTTIN 21 Đọc trong nền từ tty SIGTTOU 22 Viết trong nền lên tty TSTP 24 Tín hiệu <Ctrl>+<z> SIGSYS 31 Gọi từ hệ thống không đúng
Ngoài ra BASH còn hỗ trợ các tín hiệu:
-
DEBUG :Tín
hiệu
thường
dùng
để
gỡ
lỗi,
[khối_Lệnh]
thi hành sau mỗi lệnh được thực thi ngoại trừ các dòng chú thích - ERR: Khối lệnh được thi hành mỗi khi một mệnh lệnh trong văn lệnh trả về giá trị khác 0. Tuy nhiên, tín hiệu này không được thực thi nếu các lệnh trả về giá trị khác 0 đó nằm bên trong vòng lặp, câu lệnh phân nhánh hay trong biểu thức có && || và !
Thí dụ: Lệnh sau đây khi đặt vào trong một văn lệnh sẽ làm mất hiệu lực của tổ hợp phím bấm <Ctrl>+<c> để ngừng chương trình:
trap 2 # Ignore Control-C key stroke
Tương
tự
như
vậy,
nhưng
mệnh
lệnh
sau
sẽ
hiển
thị
dòng
chữ
"Control-C
was
pressed!"
mỗi
khi
nó
bẩy
được
tín
hiệu
số
2
trap 'echo "Control-C was pressed!"' 2 # Message when Control-C pressed.
Thí dụ2: Đoạn mã sau đây sẽ chỉ cách để ngăn chận việc bấm tổ hợp <Ctrl>+<c> trong một giai đoạn cần thiết nào đó và sau đó trả lại trạng thái bình thường cho chương trình
trap 2 # Control-C, now disabled. command1 command2 command3 ... trap 2 # Control-C re-enabled command5 command6 ...
Thí
dụ3:
chương
trình
test
sau
dây
sẽ
gọi
chưong
trình
test2
như
là
một
tiến
trình
con
và
sau
đó
5
giây
tiến
trình
mẹ
(test
)
dự
định
hủy
tiến
trình
con
này
bằng
lệnh
kill
với
tín
hiệu
16
nhưng
trong
test2
đã
có
bẫy
để
ngăn
không
cho
nó
phép
ngưng
chạy
bằng
tín
hiệu
16
này
(dùng
trong
các
trường
hợp
chạy
chương
trình
con
trong
thời
điểm
nghiêm
trọng
không
thể
ngưng).
test2
sẽ
tự
kết
thúc
sau
10
giây.
Nội dung của tập tin test
#!/bin/bash #file name: test ./test2 & #call test2 as a child in background pid=$! #record process ID of test2 echo "The PID of child process: $pid"
i=0 while [ $i -lt 5 ]; do sleep 1 echo -n ". " let "i +=1" done
echo -e "\nAttempt to kill the child process and release signal 16 by 'kill' command:" kill -16 $pid
Nội dung tập tin test2
#!/bin/bash #File name test2 i=0 trap 'echo "some body tried to kill my process through signal 16"' 16 echo "Verifying child process number:`pgrep test2`" while [ $i -lt 10 ]; do sleep 1 " let "i += 1" echo -n "+" done echo "I exit myseft now"
Thí dụ4: Truyền tín hiệu 2 (tức là tổ hợp phím bấm <Ctrl>+<c>) từ parent sang cho child xử lí
#!/bin/bash #parent echo parent running trap 'echo parent exiting; exit' 0 trap :2 ./child & sleep 1000
#!/bin/bash #child echo child started. pid is $$ trap 'echo child got signal 2; exit' INT sleep 1000
umask
[-p]
[-S]
[
[số_cơ_chế]
]
[sửa]
Nếu
như
lệnh
chmod
cho
phép
thay
đổi
hay
xác
định
quyền
truy
cập
của
các
tập
tin
thì
lệnh
umask
ngược
lại
xác
định
giới
hạn
truy
cập
của
một
tập
tin.
Các
giá
trị
[số_cơ_chế]
bằng
số
trong
lệnh
umask
chính
là
giá
trị
bù
của
giá
trị
quyền
truy
cập
trong
cơ
số
bát
phân.
Sau
khi
một
lệnh
umask
được
thực
thi,
thì
tất
cả
các
tập
tin
tạo
ra
trong
khi
thi
hành
văn
lệnh
(chẳng
hạn
qua
các
lệnh
chuyển
hướng)
đều
sẽ
phải
có
giá
trị
truy
cập
theo
đúng
giới
hạn
của
umask
.
Ngoài
ra,
lệnh
umask
sẽ
có
giá
trị
hiệu
lực
kể
từ
khi
nó
được
định
nghiã
cho
dến
khi
có
lệnh
umask
mới
hay
kết
thúc
văn
lệnh
Thí
dụ:
Thí
dụ
sau
sẽ
tạo
ra
tập
tin
mylog.txt
có
giá
trị
quyền
truy
cập
là
755
(tức
là
giá
trị
bù
của
nó
khi
ra
lệnh
umask
là
022
)
#!/bin/bash umask 022 tail -n 10 myfile > mylog.txt
Lưu ý: giá trị umask thông dụng khác là 177, với giá trị này thì các tập tin được tạo ra trong văn lệnh sẽ có phép truy cập là 600 (tức là chỉ cho người tạo ra nó hay root có quyền viết/đọc tập tin.)
Các tham số:
-
-S
Nếu không có số cơ chế thì lệnh sẽ hiển thị các giá trị cài đặt hiện tại dạng kí tự -
-p
Yêu cầu hiển thị lệnh umask hiện đang có hiệu lực. -
Nếu
ra
lệnh
umask
không
có
tham
số
đi
kèm
thì
giá
trị
[số_cơ_chế]
được hiển thị
command [Lệnh] [Tham_Số_Lệnh]
[sửa]
Thực
thi
[Lệnh] [Tham_Số_Lệnh]
bỏ
qua
các
hàm
có
cùng
tên.
Chỉ
có
những
lệnh
sẵn
có
hay
các
lệnh
tìm
thấy
được
trong
$PATH
là
có
thể
được
thi
hành.
Trường
hợp
này
thường
được
dùng
khi
trong
văn
lệnh
có
định
nghiã
hàm
trùng
tên
với
một
lệnh
nào
đó
của
BASH
hay
ở
bên
ngoài.
Lệnh
này
trả
về
giá
trị
127
nếu
[Lệnh]
không
tìm
thấy
hay
nếu
có
lỗi
xãy
ra.
enable
[-n]
[-p]
[-a] [Tên]
[sửa]
Dùng
để
hoạt
hoá
(enable)
hay
ngăn
trở
(diable)
các
lệnh
có
sẵn
của
BASH.
Việc
ngăn
trở
của
một
lệnh
BASH
sẽ
cho
phép
một
lệnh
nào
khác
có
cùng
tên
được
thực
thi
mà
không
cần
phải
ghi
rõ
tên
đầy
đủ
của
lệnh
đó
(nếu
nó
được
lưu
trong
thư
mục
có
tên
trong
$PATH
).
Bình
thường
thì
BASH
sẽ
thực
thi
lệnh
sẵn
có
của
nó
và
nếu
có
lệnh
khác
cùng
tên,
mà
không
được
gọi
bằng
tên
đầy
đủ,
thì
lệnh
này
sẽ
không
được
thực
thi
(vì
có
mức
ưu
tiên
thấp
hơn) .
Các tham số bao gồm:
-
-n
dùng để ngăn trở lệnh sãn có của BASH có tên là[Tên]
-
-p
Hiển thi danh mục các lệnh sẵn có của BASH đang được hoạt hoá -
-a
HIển thị danh mục các lệnh sẵn có của BASH với thông báo đính kèm lệnh nào được hoạt hoá và lệnh nào đang bị ngăn trở -
Khi
gọi
lệnh
mà
không
cung
cấp
bất
kì
[Tên]
hay tham số nào thì tương đuơng với việc dùng tham số-p
Lệnh
trả
về
0
trừ
khi
[Tên]
không
phải
là
lệnh
sẵn
có
hay
có
lỗi
khi
gọi
enable
.
help
[
[TÊN]
]
[-s
[Dạng_thức]
]
[sửa]
HIển
thị
các
thông
tin
hỗ
trợ
về
cách
dùng
của
các
lệnh
sẵn
có
[TÊN]
.
Nếu
dùng
tham
số
-s
thì
lệnh
này
sẽ
hiển
thị
cách
dùng
của
các
lệnh
nào
có
tên
tương
thích
với
[Dạng_thức]
và
lệnh
trả
về
0
trừ
khi
không
có
lệnh
sẵn
có
nào
thoả
mãn
[TÊN]
hay
[Dạng_thức]
printf [Định_Dạng]
[
[Các
Đối_Số]
]
[sửa]
Đây
là
lệnh
mô
phỏng
theo
lệnh
printf
trong
C.
Lệnh
này
hiển
thị
dòng
văn
bản
được
định
dạng
theo
sự
sắp
xắp
của
người
lập
trình.
[Định_Dạng]
là
một
string
bao
gồm
3
loại
đối
tượng:
- Các chuỗi kí tự thông thường, sẽ được chép thẳng ra stdout
- Các dãy kí tự thoát theo chuẩn ANSI C, được hoán chuyển theo đúng ý nghiã và đưa ra stdout
- Các đặc tả định dạng mà mỗi đặc tả sẽ được nhập vào từ một đối số tương ứng theo thứ tự.
Ngoài ra,
-
%%
sẽ cho hiển thị dấu%
-
%b
Hiển thị string với việc chuyển dịch đối số tương ứng, và nếu đối số có kí tự thoát theo chuẩn ANSI sang đúng ý nghiã của nó. -
%s
Hiển thị đối số tương ứng. -
%q
HIển thị đối số tương tứng trong dạng mà nó có thể được dùng như là ngỏ vào của hệ vỏ
Thí dụ:
PI=3.1415926 Dec=31373 Msg1="\tHello" Msg2="world"
printf "PI with 2 decimal = %1.2f" $PI printf "Pi to 5 decimal places and round off = %1.5" $PI printf "Constant = \t%d\n" $DecimalConstant # Inserts tab (\t). printf "%b %s \n" $Msg1 $Msg2
read
[-rs]
[-a
[Tên_Mảng]
]
[-d
[Kí_tự_giới_hạn]
]
[-n
[n]
]
[-t
[Thời_hạn]
]
[-u
[fd]
]
[
[Danh_Sách_Biến]
]
[sửa]
Đọc
một
dòng
từ
ngỏ
vào
chuẩn
hay
từ
bộ
mô
tả
tập
tin
df
nếu
dùng
tham
số
-u
,
và
từ
(word)
đầu
tiên
sẽ
được
gán
lên
biến
đầu
tiên,
từ
thứ
hai
đuợc
gán
cho
biến
thứ
hai
...,
tất
cả
phần
còn
lại
của
dòng
sẽ
được
gán
hết
lên
biến
cuối
cùng
nếu
không
đủ
số
biến
trong
[Danh_Sách_Biến]
.
Ngược
lại,
nếu
số
biến
trong
Bản
mẫu:Danh
Sách
Biến
nhiều
hơn
số
từ
có
trong
dòng
đang
xử
lí
thì
các
biến
đi
sau
sẽ
đưọc
gán
gía
trị
null
.
Các
kí
tự
trong
$IFS
sẽ
được
dùng
làm
kí
tự
giới
hạn.
Kí
tự
thoát,
dấu
nghiên
về
\
,
có
thể
dùng
để
xóa
các
ý
nghiã
đặc
biệt
của
kí
tự
liền
ngay
sau
nó
và
dùng
để
đọc
dòng
nối
tiếp
nếu
đi
đôi
với
dấu
đầu
dòng.
Nếu
[Danh_Sách_Biến]
không
được
cung
ứng
thì
read
sẽ
gán
toàn
bộ
giá
trị
của
dòng
đọc
được
cho
biến
REPLY
.
Lệnh
sẽ
trả
về
0
trừ
khi
gặp
kí
tự
EOF
,
read
bị
hết
thời
hạn
đợi
khi
đọc,
hay
bộ
mô
tả
tập
tin
không
hợp
lệ
khi
dùng
tham
số
-u
.
Các tham số quan trọng:
-
-a [Tên_Mảng]
:Các từ đọc được sẽ được gán lên các phần tử của biến kiểu có kiểu mảng[Tên_Mảng]
bắt đầu từ chỉ số 0. Tất cả các phần tử sãn có của mảng sẽ bị xoá. Tất cả các biến tham số khác sẽ bị bỏ qua khi dùng cách này. -
-d [Kí_tự_giới_hạn]
:Dùng[Kí_tự_giới_hạn]
để kết thúc cho dòng nhập vào thay vì dùng kí tự đầu dòng. -
-n [n]
: lệnhread
sẽ ngừng đọc sau khi đọc được[n]
kí tự thay vì đợi đến hết dòng nhập vào -
-r
:tham số này sẽ hủy bỏ vai trò kí tự thoát của\
và dấu nghiên về nay chỉ đóng vai trò bộ phận của dòng nhập vào. Đặc biệt khi dùng nó thì cặp kí tự "dấu nghiên về-dấu đầu dòng" sẽ không còn giá trị như là sự nối tiếp của dòng văn bản nữa. -
-s
: Chế độ lặng lẽ (silent mode) Nếu ngỏ vào là một đầu cuối thì các kí tự sẽ không được hiển thị lập lại -
-t [Thời_hạn]
cung ứng thời gian tối đa và trả về lỗi nếu toàn bộ dòng nhập không đọc được trong thời hạn[Thời_hạn]
giây. Tham số này không có hiệu lực nếu lệnh read không nhập dữ liệu từ đầu cuối hay từ một ống dẫn truyền. -
-u [fd]
Ngỏ nhập lấy từ bộ mô tả tập tin[fd]
Thí dụ sau đây trích ra từ Advanced Bash-Scripting Guide:
# A single 'read' statement can set multiple variables. echo -n "Enter the values of variables 'var2' and 'var3' (separated by a space or tab): " read var2 var3 echo "var2 = $var2 var3 = $var3" # If you input only one value, the other variable(s) will remain unset (null).
echo -n "Enter another value: " read # No variable supplied for 'read', therefore... #+ Input to 'read' assigned to default variable, $REPLY. var="$REPLY" echo "\"var\" = "$var""<BR
echo "Enter a string terminated by a \\, then press <ENTER>." echo "Then, enter a second string, and again press <ENTER>." read var1 # The "\" suppresses the newline, when reading $var1. # first line \ # second line
echo "var1 = $var1"
echo 1234567890 > File # Write string to "File". exec 3<> File # Open "File" and assign fd 3 to it. read -n 4 <&3 # Read only 4 characters. echo -n . >&3 # Write a decimal point there. exec 3>&- # Close fd 3. cat File # ==> 1234.67890 exit 0
type
[-afptP]
[
[Danh_Sách_Tên]
]
[sửa]
BASH
chỉ
ra
việc
sử
dụng
của
mỗi
tên
trong
[Danh_Sách_Tên]
là
thuộc
loại
nào
trong
các
loại
function,
builtin,
file,
alias,
hay
keyword
.
Các tham số có thể dùng bao gồm:
-
-p
: Trả về tên của tập tin trong ổ mà sẽ được thực thi -
-P
:Buộc tìm trong $PATH cho mỗi tên trong <danh sách> -
-a
: Trả về tất cả các nơi mà chứa tập tin khả thi có cùng tên tập tin. NÓ bao gồm cả hàm và alias -
-r
: Không tìm tên hàm, và các lệnh có sẵn
Trả về 0 nếu tên bất kì trong danh sách được tìm thấy, trả về khác không ngược lại
ulimit
[-acdflmnpstuvSH]limit]
[sửa]
Cho phép điều chỉnh các tài nguyên dành cho các tiến trình khởi động bởi trình bao.
Các tham số sau có ý nghiã:
-
-S
:Thay đổi và báo cáo giới hạn mềm liên hệ đến một tài nguyên; các giá trị có thể làhard, soft
, vàunlimited
-
-H
:Thay đổi và báo cáo giới hạn cứng liên hệ đến một tài nguyên -
-a
:Báo cáo tất cả giới hạn hiện có -
-c
:Độ lớn tối đa của các tập tin cốt lõi được tạo ra -
-d
:Độ lớn tối đa của phân đoạn (segment) dữ liệu của một tiến trình -
-f
:Độ lớn tối đa của một tập tin tạo được bởi trình bao. -
-l
:Độ lớn tối đa có thể được khoá (lock) vào trong bộ nhớ -
-n
:Độ lớn tối đa số tập tin có thể mở được -
-p
:Độ lớn của bộ đệm kiểu ống dẫn, đơn vị dùng là một khối 512 bit -
-s
:Độ lớn tối đa của chồng xếp -
-t
:Độ lớn tối đa của thời gian xử lí trong CPU tính bằng giây -
-u
:Số tối đa các tiến trình mà có thể cung ứng cho một người dùng -
-v
:Độ lớn tối đa của bộ nhớa ảo có thể cung cấp cho một tiến trình
Đơn vị sử dụng cho các báo cáo liên quan đến tập tin là 1024 byte
Lệnh trả về 0 trừ khi có tham số không hợp lệ hay lỗi xãy ra khi cài đặt giới hạn mới.
Quá trình Truy sửa lỗi trong BASH[sửa]
Chúng ta đã xem xét qua phần nội dung chính các hỗ trợ của văn lệnh BASH. Đó là các kiến thức thuộc về lí thuyết. Trong thực tế, việc viết mã cho một chương trình nghiêm túc thường chỉ chiếm tối đa một phần ba tổng thời gian từ khi tạo ra mà nguòn đến khi thực sự dùng nó. Để thay cho phần tổng kết lý thuyết, chúng ta hãy tìm hiểu thêm vài chia sẽ trong quá trình truy sửa lỗi (debugging)
Một số dạng lỗi cơ bản[sửa]
Trong các phiên bản chuẩn hiện tại của BASH không cung cấp bất kì một phưong tiện đạc biệt nào để truy sửa lỗi (debug) . Một số cách thực để tiến hành quá trình này đều do người lập trình tự tạo nên hay dùng phần mềm hỗ trợ như phần bashdb
Các dạng lỗi chung trong văn lệnh BASH thường bao gồm:
Lỗi chính tả[sửa]
Đây
là
dạng
lỗi
thông
thường
và
cũng
dể
chữa
nhất.
Việc
việt
sai
thường
sẽ
bị
trình
dịch
bắt
lỗi
ngay
lập
tức
và
thông
bao
lỗi
thường
ở
dạng
syntax
error
hay
command
not
found
hay
unexpect
end
of
file
.
Một
trong
những
nguyên
do
là
có
thể
người
gõ
phím
tạo
lỗi
gõ
hay
do
thói
quen.
Thói
quen
này
hình
thành
trong
việc
sử
dụng
các
ngôn
ngữ
khác.
Thí
dụ
mã
VB
sẽ
không
chiếu
cố
nhiều
đến
việc
chưà
khoảng
trống
là
bao
nhiêu
trong
biểu
thức
logic,
hay
mã
Pascal
không
để
ý
đến
việc
viết
hoa
hay
viết
thường.
Tuy
nhiên,
trong
một
số
trường
hợp
một
tên
viết
sai
chính
tả
nhưng
vẩn
hợp
lệ
thì
việc
truy
lỗi
sẽ
khó
hơn
nhiều.
Thí dụ :
if [ "$1"="a" ]; then ....fi #missing one space character between = sign WHILE [ $x = 1 ]; DO .... DONE #all BASH commands are in lowercase; do not capitalized! $x = 1 # Wrong! To assign, use: x = 1 instead
Lỗi văn phong[sửa]
Lỗi văn phong là tất cả các dạng lỗi liên quan đến nội dung của các biến. Việc gán hay dùng sai kiểu trên 1 loại biến đã định nghiã kiểu cũng thuộc loại này nhưng hay thấy nhất là việc sử dụng sai hay thiếu các dấu ngoặc, các kí tự thoát của nội dung một biến string
Thí dụ:
echo number 8 > $1 # should be quoted. e.g. echo "number 8 > $1" cat mycodefile | grep myvar=$1 # intent to find lines content word myvar=$1 in mycodefile; #iit should rewite as: cat mycodefile | grep "myvar=\$1"' echo 'value of variable x is $x' # wrong quotation; should be echo "value of x is $x" echo "he said: "it is OK!", but is is really not OK." # Should change to echo -e "he said: \"it is OK!\", but it is really not OK."
Lỗi thiếu, thừa hay dùng sai từ khoá[sửa]
Các lỗi dạng này có thể xãy ra trong các câu lệnh phân nhánh.
Thí dụ: Thay vì
if .... elif ... else ... fi
Thì viết thành
if ... else if ... else ... fi
Hay là viết sai thành
if .... elif .... else .... if
Dùng cùng tên đã có sẵn từ trước[sửa]
Một khi việc này xãy ra, thường thì trình dịch sẽ có thể không phát hiện được lỗi loại này. Ứng xử của chương trình sẽ còn tuỳ thuộc vào cách thiết lập mã và các giá trị của sự trùng lặp tên này ảnh hưởng như thế nào đến dữ liệu xử lí cũng như đến môi trường xử lí. Nên tránh tối đa dùng lại các tên của các hàm, các biến môi trường và các lệnh chuẩn của BASH vì như vậy sẽ buộc trình dịch hoặc bắt lỗi hoặc sử dụng tên có thứ tự ưu tiên cao hơn để gọi trước (trừ khi dùng các lệnh đặc thù xử lí loại này). Việc đặt tên biến trùng với các từ khoá sẽ chắc chắn bị trình dịch phát hiện và báo lỗi. Ngoài ra, trong chừng mực nào đó phải nhớ rõ các biến đã được xuất thành biến toàn cục cũng như các loại biến đã khai là biến địa phương thì không nên lạm dụng sai chức năng sẽ có thể gây hậu quả khôn lường. Việc đặt tên biến tránh không bị "đụng hàng" thì khá dể dàng mà ngưòi lập trình có thể có các kĩ năng riêng để làm chuyện này.
Ghi chú:
- Các lệnh chuẩn thường chứa trong các thư mục tên bin hay sbin như là /bin, /sbin, /usr/bin, /usr/sbin. Có thể kiểm thêm giá trị của $PATH để tìn ra các tên lệnh khác trong hệ thống
- Các tên lệnh sẵn có và các từ khoá trong BASH có thể dể dàng tìm thấy các taì liệu tham chiếu BASH
- Các biến môi trường có thể được hiển thị hầu hết qua lệnh env
- Các lệnh như hash và command có thể ảnh hưởng hay thay đổi đến thứ tự ưu tiên chuẩn của các mà BASH gọi một lệnh có trùng tên
Dùng sai/lầm lẫn cú pháp lệnh[sửa]
Mỗi
lệnh
trong
Linux
đều
có
các
điều
kiện
về
tham
số
và
cú
pháp
riêng.
Thông
thường,
các
lệnh
đó
sẽ
phát
hiện
ra
những
lỗi
thô
thiển
như
là
sai
cú
pháp
thiếu
hay
thừa
các
tham
số,
tham
số
quá
hạn
mức
(out
of
range
),
...
Việc
thông
báo
lỗi
sẽ
hiển
thị
thông
qua
ngỏ
ra
chuẩn
(thường
là
stderr
)
hay
trả
về
các
giá
trị
khác
0.
Đối
với
các
lệnh
hỗ
trợ
biểu
thức
chính
quy
mở
rộng
(như
grep,
awk,
sed,
...
)
thì
người
dùng
nên
đọc
lại
các
siêu
kí
tự
mà
lệnh
hỗ
trợ;
chúng
có
thể
không
hoàn
toàn
có
cùng
ý
nghiã
cho
mỗi
lệnh..
Ngoài
ra,
cần
lưu
ý
có
trường
hợp
phải
dùng
các
tham
số
đặc
biệt
để
mở
hay
đó
một
chức
năng
nào
đó
của
lệnh
(thí
dụ
muốn
hỗ
trợ
biểu
thức
chính
quy
mở
rộng
trong
grep
thì
phải
dùng
tham
số
-E
).
Trong
trường
hợp
người
lập
trình
dựng
lệnh
dạng
script
dựa
trên
lệnh
getops
thì
việc
gọi
nó
càng
phải
cẩn
thận
hơn.
Thí dụ sau đây sẽ cho thấy điều đó
#!/bin/bash #content of mycmd Parama= Paramb= Parama_Value= while getopts a:b name do case $name in a) Parama=1" Parama_Value="$OPTARG" echo "Parama flag is $Parama, and its value is $Parama_Value" ;; b) Paramb=1 echo "Paramb flag is $Paramb" ;; *) echo "Usage: `basename $0`: [-a value] [-b]\n" exit 2 ;; esac done
Lệnh
mycmd
trên
đòi
hỏi
tham
số
a
phải
có
giá
trị
đi
kèm.
Khi
gọi,
nếu
người
lập
trình
"quên"
không
cung
ứng
thì
hậu
qủa
sẽ
có
thể
sai
Chẳng
hạn,
dự
định
gọi
lệnh
mycmd
với
hai
tham
số
-a
và
-b
nhưng
vì
thiếu
giá
trị
cho
tham
số
-a
nên
mycmd
đã
hiểu
"lầm"
rằng
"-b
"
là
giá
trị
của
tham
số
-a
và
tham
số
-b
có
giá
trị
null
#./mycmd -a -b #Parama flag is 1, and its value is -b
Để
khắc
phục
nhược
điểm
này
thì
người
lập
trình
có
thể
thêm
vào
mycmd
một
vài
câu
lệnh
kiểm
nghiệm
tham
số
nhằm
phát
hiện
sớm
hơn
ngay
từ
bên
trong
mycmd
mỗi
khi
tham
số
-a
thiếu
giá
trị
Viết sai cấu trúc logic của các mệnh đề[sửa]
Việc viết sai logic của các mệnh đề hay trình bày sai nội dung của một mệnh đề sẽ gây ra các lỗi mà trình dịch không thể tìm ra. Các lỗi này thường là do người lập trình không kiểm soát kĩ các mệnh đề logic hay dùng mệnh đề logic với quá nhiều phép toán logic nối nhau. Cách tốt nhất để tránh lỗi loại này là đừng bao giờ dùng một mệnh đề có nhiều hơn hai phép toán logic. Vì Nhiều hơn thế, số tổ hợp logic của bảng chân trị sẽ lên đến 8 hay hơn các trường hợp khả dĩ, và như thế gây khó khăn cho việc kiểm nghiệm nếu không muốn mất thì giờ. Thay vì vậy, người ta có thể chia mụch tiêu thành nhiều phân nhánh nhỏ mỗi phân nhánh xử lý 1 hay hai trường hợp Logic là tốt nhất.
Một dạng hiếm thấy hơn là hiểu sai về cách trình bày của mệnh đề
Thí dụ Để xoá tất cả các tập tin trong thư mục hiện hoạt mà nội dung tên của có có kí tự khoảng trống (space). Người ta có thể làm một cách sai sót như sau:
ls | grep ' ' | xargs -i rm {}
Lý
do
của
sự
sai
sót
này
là:
Trong
việc
hiển
thị
các
tên
tập
tin
có
kí
tự
khoảng
trống
thì
trình
dịch
hiển
thị
các
tên
này
rất
bình
thường.
Nhưng
một
khi
các
tên
đó
được
dùng
làm
tham
số
cho
lệnh
rm
thì
lệnh
rm
sẽ
không
thể
biết
được
đó
các
thành
phần
ngăn
cách
nhau
bở
kí
tự
khoảng
trống
là
của
1
tên
duy
nhất
và
do
đó
nó
sẽ
tìm
để
xoá
các
thành
phần
này
thay
vì
tên
đúng
của
tập
tin.
Để
điều
chỉnh
có
thể
thử
dùng
sed
thay
vì
grep
ls | sed -n '/ /s/ /\\ /gp' | xargs -i rm {}
Thiết lập sai dòng điều khiển[sửa]
Trình dịch không có khả năng phát hiện lỗi kiểu này. Việc này thuộc về cách thức kiến trúc một chương trình của người viết mã. Cách tốt nhất đối với các chương trình nhỏ, người ta có thể dùng lưu đồ (flow chart) để kiểm tra các thuật toán, việc xử lí toàn vẹn các dạng dữ liệu, hay các cách thức xử lí của chương trình. Đối với các chương trình cở trung bình (vài trăm dòng lệnh trở lên) và lớn (từ vài ngàn dòng lệnh) thì cách tốt nhất là phải có các sơ đồ khối để phân chia chức năng và kiểm tra hoạt động của từng khối. Tuỳ theo mức phức tạp, các khối này có thể bao gồm nhiều sơ đồ khối con nhỏ hơn nhằm lo liệu các phần riềng lẽ của khối lớn. Cuối cùng, các đơn vị nhỏ nhất có thể là các lưu đồ logic. Viết 1 chương trình cở trung bình và cở lớn thường tốn thời gian để kiến trúc và truy sữa lỗi hơn thời gian xây dựng mã nguồn rất nhiều. Việc kiểm tra và thử nghiệm hiệu quả của một chương trình phụ thuộc vào khả năng của người (nhóm hay nhiều nhóm) tham gia. Có nhiều kĩ thuật khác nhau như là: Kiểm nghiệm kiểu hộp đen, kiểu hộp trắng, kiểm dữ liệu ngẫu nhiên, kiểm tra điều kiện biên, ...Các loại thử nghiệm này đòi hỏi người lập trình phải được trang bị hiểu biết về công nghệ phần mềm.
Vài phương cách để truy sửa lỗi[sửa]
Việc truy sửa lỗi thường cần nhiều thời gian và công sức hơn là khi phát triển mã. Do đó, người viết sẽ càng thành công và ít tốn thì giờ nếu càng có kinh nghiệm, cẩn thận trong lúc kiến trúc, thiết kế, xây dựng mã, và có biện pháp kiểm soát hay phản biện rõ ràng.
Việc phải làm đầu tiên khi có một vấn đề với chương trình là: vấn đề đó phải lập lại được bởi người truy sửa. Do đó, vấn đề được mô tả càng rõ ràng chính xác và chi tiết bao nhiêu thì việc truy sửa sẽ đỡ mất thì giờ bấy nhiêu. Trong nhiều trường hợp, vấn đề không xãy ra đối với chương trình mà là đối với việc hiểu, cách sử dụng đúng, hay việc vận hành hợp lệ của chương trình . Trong trường hợp này, cần phải bàn bạc lại với ngưòi (chủ thể) đã nêu vấn đề.
Một khi vấn đề đã được tái lập (dupplicate) và xác định là do chương trình tạo ra thì quá trình truy lỗi bắt đầu. Người truy sửa lúc này đã xác định được điều kiện và trạng thái để chương trình bị lỗi. Vấn đề còn lại là tìm ra căn nguyên (root cause) của lỗi, từ đó tìm đến dòng hay khối mã có lỗi, so sánh lại với kiến trúc hay thiết kế của chương trình để tìm ra các khắc phục.
Các phần sau đây sẽ không đề cập đến quá trình truy sửa mà chỉ chú trọng đến chi tiết các kĩ năng truy sửa lỗi khi viết văn lệnh BASH
Dựa vào thông báo của trình dịch hay của lệnh dùng[sửa]
Thông
báo
của
trình
dịch
là
phương
tiện
căn
bản
để
phân
tích
lỗi.
Hầu
hết
các
lỗi
cú
pháp
đều
bị
tìm
thấy
qua
trình
dịch.
Ngoài
ra
các
thông
báo
trả
về
qua
stderr
cùng
cho
biết
về
tình
trạng
sử
dụng
lệnh
khi
mắc
lỗi.
Có
nhiều
trường
hợp
lồi
cú
pháp
sẽ
không
báo
cáo
đúng
số
thứ
tự
dòng
lệnh
bị
lỗi.
Chẳng
hạn
như
sự
thiếu
hay
sai
trong
việc
đóng
và
mở
các
dấu
ngặc
cho
kiểu
string.
Thiếu
các
từ
khoá
hay
kí
tự
(như
là
dấu
;;
trong
lệnh
case
)
trong
việc
đóng
các
mệnh
lệnh
phân
nhánh
và
vòng
lặp
(như
là
fi,
done,
esac
...
)
sẽ
khiến
cho
trình
dịch
tìm
đến
hết
tập
tin
và
thông
báo
dạng
unexpected
end
of
file
.
Trình bày mã theo kiểu "chừa lề cho khung mã"[sửa]
Đây là phương cách hữu hiệu để thấy được các câu lệnh lồng nhau. Người lập trình có thể lợi dụng việc trình bày "chừa khoảng trống đầu dòng" để kiểm tra lại các khung mã. Các khối mã nằm càng sâu bên trong các lệnh lồng nhau thì khoảng trống đầu dòng của nó càng lớn. như vậy mõi khi khối mã bị thiếu từ khoá sẽ dể dàng kiểm tra lại chỗ nào thiếu.. với cách trình bày này người truy lỗi có thể thấy được dể dàng các phân cấp của các lệnh lồng nhau tạo điều kiện cho việc truy sửa lỗi dể hơn nhiều
Thí dụ:
=
Gửi
thông
tin
về
dữ
liệu
hiện
tại
ra
ngoài
bằng
lệnh
echo,
printf
hay
bằng
đổi
hướng
ngỏ
ra
chuẩn[sửa]
Một
trong
những
phương
các
rất
hữu
hiệu
để
truy
sửa
lỗi
là
dùng
lệnh
echo
,
lệnh
printf
,
hay
đổi
hướng
ngỏ
ra
chuẩn
lên
một
tập
tin
log
để
kiểm
nghiệm
lại
trạng
thái
hay
giá
trị
của
dữ
liệu
và
biến
(bằng
mắt
hay
bằng
các
đọc
lại
thông
tin
trong
tập
tin
log
sau
đó.)
Việc
dùng
đổi
hướng
ngỏ
ra
lên
tập
tin
có
thể
tiện
lợi
khi
lượng
thông
tin
quá
nhiều
hay
việc
xử
lý
quá
nhanh
hay
vì
lí
do
nào
đó
không
tiện
theo
dõi
trực
tiếp
diễn
biến
xử
lý
dữ
liệu
qua
màn
hình.
Các
thông
tin
này
nên
được
lắp
đặt
trên
mỗi
khối
mã
con
hay
ngay
cả
trên
từng
dòng
lệnh
(nếu
cần)
ở
khu
vực
nghi
ngờ
lỗi
bắt
đầu
xuất
hiện.
Rocky
Bernstein,
trong
tài
liệu
Advanced
Bash-Scripting
Guide
đã
cung
ứng
một
cách
thức
tinh
tế
cho
phép
người
dùng
cách
thức
để
tạo
ra
một
chế
độ
truy
sửa
lỗi.
Các
lệnh
echo
hiển
thị
các
thông
tin
trong
quá
trình
truy
sửa
chỉ
hiển
thị
khi
biến
$DEBUG
có
giá
trị
on
.
### debecho (debug-echo), by Stefano Falsetto ### ### Will echo passed parameters only if DEBUG is set to a value. ### debecho () { if [ ! -z "$DEBUG" ]; then echo "$1" >&2 # ^^^ to stderr fi }
DEBUG=on Whatever=whatnot debecho $Whatever # whatnot #turn $DEBUG on, anywhere there is debecho function, theree will ehoes
DEBUG= Whatever=notwhat debecho $Whatever # (Will not echo.) $DEBUG is off, it will not display information even the function debecho is present
Dùng
các
công
cụ
sẵn
có
trong
BASH
như
$?,
trap,
tee
[sửa]
Trong
BASH
có
một
số
lệnh
và
biến
có
thể
đưọc
dùng
trong
công
việc
truy
sửa
lỗi
đó
là
$?,
trap
,
và
tee
-
$?
Như đã biết sẽ chứa nội dung trả về của mệnh lệnh cuối cùng được thi hành. Như vậy, nếu đọc lại giá trị này thì người lập trình có thể biết được là các mệnh lệnh có bị lỗi khi thi hành hay không.
Thí dụ:
mv myfile1 myfile2 if [ ! $? = 0 ]; then echo "change name error" else echo "change name suceed!" if
-
tee
là lệnh dùng để hiển thị nội dung của ngỏ ra chuẩn đồng thời chép các hiển thị này vào một tập tin. Với cách này người ta có thể "trích ly" các thông báo ra màn hình chuẩn (hay ngược lại vào trong một tập tin) để dùng trong truy lỗi sau này
Thí
dụ:
Để
đọc
lại
PCI
ID
của
các
thiết
bị
lưu
trữ
đồng
thời
chép
thông
tin
đó
vào
tập
tin
là
mylog
thì
có
thể
dùng
lệnh
lspci -n | grep class 01 | tee mylog
-
Như
đã
đề
cập
trên
lệnh
trap
có thể dùng để "bắt" các tín hiệu trong lúc truy sửa lỗi.
Thí dụ: Đọan mã sau đây cho thấy cách hiển thị giá trị của các biến trước khi thoát khỏi chương trình:
#!/bin/bash # trap 'echo "value when exit of a = $a and b = $b"' EXIT a=1 b=2 echo "value of a is $a and b is $b" a=0 exit
Thí
dụ
sau
đây
cho
thấy
cách
hiển
thị
giá
trị
của
một
biến
tên
là
VAR
trong
mỗi
dòng
mà
văn
lệnh
thi
hành.
việc
làm
này
sẽ
tiện
lợi
nếu
muốn
đặc
biệt
theo
dõi
trạng
thái
nội
dung
của
một
biến
nào
đó
qua
từng
dòng
thi
hành
lệnh.
VAR=value
trap 'echo "the value of VAR is $VAR"' DEBUG
# rest of the script #!/bin/bash ....
Ngắt các dòng xử lí bằng việc dùng dấu ghi chú #[sửa]
Trong
nhiều
trường
hợp
việc
phát
hiện
ra
dòng
lệnh
bị
lỗi
gặp
khó
khăn.
Người
phát
triển
mã
có
thể
thử
dùng
phương
pháp
loại
suy
bằng
cách
chèn
các
dấu
ghi
chú
ở
đầu
các
dòng
lệnh
để
ngưng
không
cho
những
dòng
đó
được
thi
hành.
Có
thể
kết
hợp
cách
này
với
các
lệnh
echo
để
việc
truy
sửa
lỗi
hữu
hiệu
hơn.
Ngoài
ra,
người
ta
có
thể
truy
ra
dòng
lỗi
bằng
cách
"ghi
chú
hoá"
phân
nữa
số
dòng
mã
nếu
lồi
không
xãy
ra
trong
số
dòng
không
bị
ghi
chú
thì
dòng
lỗi
sẽ
nằm
trong
phần
mã
đã
bị
ghi
chú
và
cứ
thế
tiếp
tục
theo
cách
phân
chia
nhị
phân
cuối
cùng
dòng
lỗi
sẽ
bị
phát
hiện.
Thay đổi cung cách viết mã[sửa]
Việc tái cấu trúc lại cách viết mã cũng có thể dùng trong truy sửa lỗi nhưng việc làm này sẽ trả giá cao về thời gian. Đôi khi lỗi không thuộc về người lập trình viết mã mà là lỗi sai sót của trình dịch hay của các lệnh hệ thống đã được dùng để thực thi trong văn lệnh. Mặc dù trường hợp như vậy hiếm khi xãy ra nhưng có khi vẩn không tránh được. Cách hay nhất là dùng một cách viết khác (hay các lệnh khác) để cho máy thực thi cùng một thao tác có chức năng tương đương hay tương tự. Đôi khi sự hạn chế của lệnh sẽ gây ra bế tắc, chẳng hạn, việc xử lí trực tiếp thông qua các ống dẫn truyền có khi bất khả thi hay không đủ chức năng thì người lập trình có thể nghĩ tới việc xuất các thông tin vào trong một tập tin và xử lí tập tin đó như là một ngỏ nhập của các mệnh lệnh tiếp theo đôi khi rất hiệu quả.
Thí
dụ:
Có
một
loại
mẫu
đơn
form.txt
sẽ
được
"điền
tên"
thay
thế
cho
từ
khoá
NAME
.
Biến
này
sẽ
được
thay
giá
trị
vào
tùy
theo
biến
myName
được
gọi
trong
văn
lệnh
fillform
và
viết
ra
thành
tập
tin
myName.txt
Content of form.txt dear NAME, As your subscription to the X magazine will be expired. .... ......
Người
lập
trình
có
thể
"thử"
viết
văn
lệnh
fillform
như
sau
#!/bin/bash if [ "$1" = "" ];then echo "missing device name" exit 1 else myName="$1" fi
sed 's/NAME/$myName/g' form.txt > ./$myName.txt
Mã
nêu
trên
sẽ
không
hoạt
động
được
vì
dấu
kí
tự
'
sẽ
hạn
chế
tên
biến
$myName
được
chuyển
đổi
thành
nội
dung
đúng.
Để
khắc
phục
có
thể
thay
đổi
cách
viết
thành
sed "s/NAME/$myName/g" form.txt > ./$myName.txt
Dùng cách viết "đủ" cho các loại lệnh chẻ nhánh dòng điều khiển[sửa]
Trong các phân nhánh thường người lập trình chỉ chú ý đến dòng điều khiển mà mình quan tâm và bỏ qua không xử lí các phân nhánh còn lại. Truy nhiên trong thực tế, nhiều khi dữ liệu có lỗi được đưa tới sẽ lọt vào đúng ngay tình huống phân nhánh mà người lập trình đã bỏ qua không xử lý. Trong trường hợp này việc truy sử lỗi sẽ trở nên khó khăn hơn vì không có dòng xử lí liên quan đến trạng thái lỗi hiện tại và do đó chương trình có thể sẽ có những ứng xử bất ngờ. Cách lập trình có tính cách "tự vệ" tốt là việc "bao thầu" hết các dòng chẻ nhánh mặc dù người ta không mong muốn phân nhánh đó sẽ xãy ra.
Thí dụ sau đây dùng mã giả. Thay vì viết dạng
if [ Điều kiện1 ]; then .... if [ Điều kiện 2 ]; then .... fi ... fi
Thì điều chỉnh thành dòng chẻ đưọc xử lí "đủ" nhằm giúp cho việc truy sửa lỗi được thuận tiện hơn
if [ Điều kiện1 ]; then .... if [ Điều kiện 2 ]; then .... esle Xử lí lỗi bất ngờ hay lỗi nội bộ vi phạm điều kiện 2 (unexpectable error or internal error) fi ... else Xử lí lỗi nội bộ hay vi phạm điều kiện 1 fi
Dùng hỗ trợ khác[sửa]
Các hỗ trợ này có thể được tung ra dạng sản phẩm bán ngoài thị trường hay tự do ở dạng giấy phép GNU. Hiện tại, có một đề án hỗ trợ việc truy sửã lỗi là bashdb. Lệnh này có thể được tải về từ trang http://bashdb.sourceforge.net/.
Việc trình bày cách xử dụng thêm các công cụ bên ngoài sẽ vượt khỏi yêu cầu nội dung của loạt bài này. Nếu người đọc có mong muốn xin hãy đọc thêm các tài liệu riêng của các hỗ trợ đó.
Lập trình đa luồng (multi threading) trong BASH[sửa]
Trong BASH người ta ít khi dùng nó cho mụch đích lập trình đa luồng thay vào đó là các ngôn ngữ mạnh hơn (như C/C++). Tuy nhiên, BASH vẩn có khả năng để cho người lập trình viết trong kiểu kiến trúc này. Thí dụ của Rocky Bernstein sau đây trích từ hồ sơ mở “Advanced Bash-Scripting Guide"
# parent.sh # Running multiple processes on an SMP box. # Author: Tedman Eng
# This is the first of two scripts, #+ both of which must be present in the current working directory.
LIMIT=$1 # Total number of process to start NUMPROC=4 # Number of concurrent threads (forks?) PROCID=1 # Starting Process ID echo "My PID is $$"
function start_thread() { if [ $PROCID -le $LIMIT ] ; then ./child.sh $PROCID& let "PROCID++" else echo "Limit reached." wait exit fi }
while [ "$NUMPROC" -gt 0 ]; do start_thread; let "NUMPROC--" done
while true do trap "start_thread" SIGRTMIN done
exit 0
# ======== Second script follows ========
#!/bin/bash # child.sh # Running multiple processes on an SMP box. # This script is called by parent.sh. # Author: Tedman Eng
temp=$RANDOM index=$1 shift let "temp %= 5" let "temp += 4" echo "Starting $index Time:$temp" "$@" sleep ${temp} echo "Ending $index" kill -s SIGRTMIN $PPID
exit 0
# ======================= SCRIPT AUTHOR'S NOTES ======================= # # It's not completely bug free. # I ran it with limit = 500 and after the first few hundred iterations, #+ one of the concurrent threads disappeared! # Not sure if this is collisions from trap signals or something else. # Once the trap is received, there's a brief moment while executing the #+ trap handler but before the next trap is set. During this time, it may #+ be possible to miss a trap signal, thus miss spawning a child process. # No doubt someone may spot the bug and will be writing #+ . . . in the future. # ===================================================================== #
# ----------------------------------------------------------------------#
################################################################# # The following is the original script written by Vernia Damiano. # Unfortunately, it doesn't work properly. #################################################################
#!/bin/ bash
# Must call script with at least one integer parameter #+ (number of concurrent processes). # All other parameters are passed through to the processes started.
INDICE=8 # Total number of process to start TEMPO=5 # Maximum sleep time per process E_BADARGS=65 # No arg(s) passed to script.
if [ $# -eq 0 ] # Check for at least one argument passed to script. then echo "Usage: `basename $0` number_of_processes [passed params]" exit $E_BADARGS fi
NUMPROC=$1 # Number of concurrent process shift PARAMETRI=( "$@" ) # Parameters of each process
function avvia() { local temp local index temp=$RANDOM index=$1 shift let "temp %= $TEMPO" let "temp += 1" echo "Starting $index Time:$temp" "$@" sleep ${temp} echo "Ending $index" kill -s SIGRTMIN $$ }
function parti() { if [ $INDICE -gt 0 ] ; then avvia $INDICE "${PARAMETRI[@]}" & let "INDICE--" else trap : SIGRTMIN fi }
trap parti SIGRTMIN
while [ "$NUMPROC" -gt 0 ]; do parti; let "NUMPROC--" done
wait trap - SIGRTMIN
exit $?
: <<SCRIPT_AUTHOR_COMMENTS I had the need to run a program, with specified options, on a number of different files, using a SMP machine. So I thought [I'd] keep running a specified number of processes and start a new one each time . . . one of these terminates.
The "wait" instruction does not help, since it waits for a given process or *all* process started in background. So I wrote [this] bash script that can do the job, using the " trap" instruction. --Vernia Damiano SCRIPT_AUTHOR_COMMENTS