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

Từ VLOS
Bước tới: chuyển hướng, tìm kiếm
Chia sẻ lên facebook Chia sẻ lên twitter In trang này

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

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 -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] [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] true hoặc [Biểu_Thức1] 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 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ệu
Giá 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 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ệnh read 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 -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ụ:

Blockcode.gif

= 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

Trở về mục lục

Đọc bài kế

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

Chia sẻ lên facebook Chia sẻ lên twitter In trang này