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

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

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

Bài 4 - tính năng của ngôn ngữ: biến môi trường, tham số đặc biệt và hàm

Các biến môi trường

Biến môi trường là biến toàn cục thường đã được định nghiã từ trước giá trị của nó. Khi viết các văn lệnh bạn nên tránh dùng các tên biến trùng với tên đã được định nghiã bởi BASH. Để hiển thị tên và giá trị hiện tại của một số biến môi trường có thể dùng lệnh env. Số khác có thể không có mặt trong nội dung của $env hay chưa đuợc định nghiã. Trong trường hợp biến đã được xác định thì có thể dùng cú pháp:

echo $[TÊN_BIẾN] 

để hiển thị nội dung (chẳng hạn như lệnh echo $PS1 sẽ cho giá trị cài đặt của biến môi trường PS1)

Các biến môi trường này có thể thường trực tồn tại hoặc chỉ tồn tại trong điều kiện nào đó (như là chỉ có khi chạy một văn lệnh chẳng hạn). Giá trị của các biến môi trường quan trọng là:

$BASH

đường dẫn của tập tin trình dịch BASH (thường là /bin/bash )

$GROUPS

nhóm người dùng

$HOME

Thư mục truy cập của người dùng, thưuờng được viết tắt là ~ (xem tham số mở rộng trong BASH)

$HOSTNAME

tên của hệ thống (máy chủ)

$HOSTTYPE

hệ thống phần cứng

$IFS

Biến ngăn cách-- BASH lưu giá trị mặc định của kí tự ngăn cách giữa các miền (field) thường dùng trong các vòng lặp.(xem bài 3)

$MACHTYPE

Kiểu hệ thống (cpu-company-system)

Thí dụ:

$echo "$MACHTYPE" 
$i686-pc-linux-gnu 

$OLDPWD

tên thư mục hiện hoạt trước đây (tên cuối cùng trước khi thư mục hiện hoạt bị thay đổi). Khi mới dăng nhập thì biến này không tồn tại. Nhưng một khi người dùng đang ở thư mục A và chuyển thư mục hiện hoạt sang bất kì thư mục B nào khác thì $OLDPWD sẽ được tạo ra bởi BASH và có giá trị là A. (xem thêm $PWD ở 14.11)

$OSTYPE

tên bản nền (platform)

$PATH

nội dung bao gồm các đường dẫn để BASH tự động tìm đên và thi hành một khi có các mệnh lệnh được gọi mà không có cung cấp tên đầy đủ

$PWD

tên thư mục hiện hoạt. Khi di chuyển sang thư mục khác (bằng lệnh cd chẳng hạn) thì giá trị cũ sẽ được gán cho $OLDPWD, còn giá trị của thư mục vừa chuyển tới được cập nhật vào $PWD (xem thêm $OLDPWD ở 14.8)

$PPID

ID của tiến trình của chương trình cha mẹ đang chạy (Trong một văn lệnh đang chạy thì $PPID là số tiến trình của BASH đang dùng để chạy văn lệnh đó)

$RANDOM

Biến này sẽ chứa một giá trị ngẫu nhiên khác nhau mỗi lần nó được truy cập. Số ngẫu nhiên này là số nguyên từ 0 tới 23767 (thực chất đây là một số giả ngẫu nhiên (pseudo-randomize number) is an integer in rang 0..32767

Thí dụ:

MyNum1="$RANDOM"
MyNum2="$RANDOM"
echo "the values are $MyNum1 $MyNum2"

$SECONDS

số thời gian tính bằng giây mà văn lệnh đã chạy

$UID

ID của người dùng

$CPU

CPU trên máy

$LOGNAME

Tên người dăng nhập

$USER

Tên người dùng

$SHELL

tên đầy đủ của trình bao gán cho người dùng

$PS1

Dòng hiển thị của dấu nhắc (prompt) sơ cấp. Giá trị của biến này sẽ quy định cách thức và nội dung hiển thị.  Thí dụ: SuSE SLES 9 có thể cài mặc định dòng nhắc là h:w # Tức là nó sẽ cho ra dòng như là

[Tên_máy_chủ>]:[Tên_thư_mục_hiện_hoạt] #.  

Bạn có thể cài đặt lại dòng hiển thị này bằng cách gán giá trị mới cho IPS. Thí dụ: để dòng hiển thị báo ngày hiện tại và thư mục hiện hoạt thì có thể dùng lệnh

IPS="\d-\w #"
  • \d ngày tháng
  • \D{<định_dạng>} Dòng strftime(3) sẽ đưa gian hiện tại vào; nếu <định_dạng> không có thì sẽ hiện thị theo cách cài đặt thời gian theo địa phương. Dấu ngoặc { } phải có mặt để BASH hiểu tham số này (Xem thêm chi tiết về cách định dạng dòng thời gian xuất ra bởi lệnh man 3 strftime). sau đây là một số giá trị có thể dùng của PS1
  • \e Kí tự thoát.
  • \h Tên máy chủ (hostname) cho đến dấu chấm . đầu tiên.
  • \H Tên máy chủ.
  • \l Tên của thiết bị đầu cuối chạy trình bao
  • \n Kí tự đầu dòng.
  • \r Kí tự xuống hàng.
  • \s Tên của văn lệnh (giá trị tên không đầy đủ của $0)
  • \t Thời gian, kiểu 24-giờ dạng HH:MM:SS.
  • \T Thời gian, kiểu 12-giờ dạng HH:MM:SSt.
  • \@ Thời gian, kiểu 12-giờ dạng am/pm.
  • \A Thời gian, kiểu 24-giờ dạng HH:MM.
  • \u Tên tài khoảng của người đang dùng.
  • \v Số phiên bản của Bash
  • \V Số phiên bản phát hành của Bash, version + patchlevel (e.g., 2.00.0)
  • \w Tên thư mục hiện hoạt.
  • \W Tên không đầy đủ của $PWD.
  • \! Sử số (history number) của lệnh này.
  • \# Lệnh số (command number) của lệnh này.
  • \nnn Kí tự trong mã ASCII mà giá trị bát phân là nnn.
  • \\ Dấu nghiên về (backslash)
  • \[ Kí tự mở đầu của một dãy kí tự không in được (non-printing character). Có thể dùng để nhúng (embed) dãy điều khiển đầu cuối vào trong dòng nhắc (prompt string).
  • \] Kí tự kết thúc của một dãy kí tự không in được.

$PS2

Dòng nhắc thứ cấp. Gíá trị mặc định là  >. Biến này thường thấy hiển thị trên thiết bị đầu cuối mỗi khi người dùng gõ xong dòng lệnh nhưng vẩn chưa kết thúc đầy đủ. Thí dụ nếu người dùng gõ lệnh myvar=test; echo "myvar (thiếu một dấu ngoặc kép) thì BASH sẽ hiển thị dòng nhắc thứ cấp. (tức là dấu >) để kết thúc người dùng có thể gõ thêm dấu " hay kết thúc lệnh đó bằng tổ hợp phím <Ctrl>+<c>

$BASH_VERSION

Số phiên bản đầy đủ của thực thể BASH.

BASH_VERSINFO

là mảng các hằng lưu giữ thông tin về phiên bản của thực thể BASH các giá trị bao gồm:

  • $BASH_VERSINFO[0] Chính số của phiên bản (bản phát hành).
  • $BASH_VERSINFO[1] Phụ số của phiên bản (bản phát hành).
  • $BASH_VERSINFO[2] Cấp che vá.
  • $BASH_VERSINFO[3] Phiên bản dựng (build version)
  • $BASH_VERSINFO[4] Tình Trạng phát hành (thí dụ beta1).
  • $BASH_VERSINFO[5] Giá tri- của biến MACHTYPE.

$FUNCNAME

Tên của hàm đang được thi hành bên trong trình bao. Biến này chỉ tồn tại trong khi một hàm đang được thực thi. Không thể gán giá trị mới cho nó (sẽ nhận về trạng thái lỗi)

$LINENO

Số thứ tự của dòng hiện tại trong văn lệnh hay của hàm (của trình bao) đang được thực thi

$PROMPT_COMMAND

Nếu được cài thì giá trị của biến này sẽ được hiểu như là một mệnh lệnh để thi hành trước khi hiển thị dòng nhắc sơ cấp ($PS1).

$PS3

giá trị của biến này chỉ dùng trong việc hiển thị của lệnh select. Giá trị mặc định là #?

$SHLVL

Sẽ tăng 1 đơn vị mỗi lần có một thực thể của BASH được bắt đầu. Biến này chủ ý được dùng để tính độ sâu như thế nào mà các trình bao BASH lồng nhau.

 $TMOUT

Nếu được cài giá trị lớn hơn 0 thì, TMOUT sẽ trở thành thời gian hết hạn mặc định cho việc đọc (từ console chẳng hạn).

Lệnh select sẽ kết thúc nếu nó không nhận đưọc dữ liệu nhập từ thiết bị đầu cuối sau khoảng thời gian $TMOUT giây.

Trong các hệ vỏ tương tác, gía trị này là số giây để đợi dữ liệu nhập sau một lệnh dòng nhắc. BASH sẽ ngừng sau số giây $TMUOT nếu mà dữ liệu nhập chưa đến.

Tham số đặc biệt quan trọng (phân biệt với lệnh dạng kí tự)

Các tham số đặc biệt này chỉ được dùng để đọc giá trị và BASH không cho phép gán giá trị lên chúng.

0, 1, 2 ,...

các tham số của văn lệnh (hay của hàm) từ vị trí 0, 1, 2...nếu nó được gọi. BASH cung cấp tối da 10 giá tri (từ $0 tới $9). Trong trường hợp gọi một văn lệnh thì $0 là tên của văn lệnh, $1 là giá trị của tham số đầu tiên, $2 là giá trị của tham số thứ nhì,.... Ngoài ra, ngoại trừ giá trị $0 không đổi, các tên $[n] (n > 0) sẽ nhận giá trị chứa trong $[n+1] sau mỗi lần thi hành lệnh shift. Do đó, nếu gọi giá trị $[max], trong đó max là tên biến lớn nhất chứa giá trị khác null, sau mỗi lệnh shift sẽ chưá giá trị null (vì giá trị khác null của nó đã chuyển cho $[max-1]

Thí dụ:

#the affect of 'shift' on $1, $2, $3,...
#!/bin/bash
# file name: ShiftTest
echo "the values of parameters are: $0, $1, $2, $3, $4, and $5"
shift
echo "after calling shift, their values become: $0, $1, $2, $3, and $4"
After saving under the name ShiftTest, issue commands:
#chmod 755 ./ShiftTest 
#./ShiftTest one two three four five
The Scipt will display as
#the values of paramaters are: one, two, three, four, and five
#after calling shift their values become: two, three, four, and five

#

biến này chứa tổng số tham số có giá trị khác null khi văn lệnh được gọi.

?

Biến chứa mã thoát của mệnh lệnh cuối cùng trong văn lệnh trả về (giá trị này thường là 0 nếu mệnh lệnh thi hành thành công)

Thí dụ:

echo "bash location :$BASH"
echo "number of argument when calling this script :$#"
if [ ! $# -eq 2 ]; then                   # if there are not 2 arguments then 
      echo "too many parameters given"    #display error
      exit 1                              #Exit code = 1
else
      echo "now we copy file $1 to $2"
      cp -f $1 $2 1>/dev/null 2>/dev/null #copy file but suppress output messages
      if [ $? = 0 ]; then
             echo "copied."
      else
            echo "error while copying with the error code $?"
     fi
fi

*

Cho danh sách các tham số vị trí, bắt đầu từ vị trí 1. Như vậy $* sẽ là danh sác các tham số được ngăn cách nhau bởi dấu ngăn cách $IFS. Nếu để cài đặt mặc định thì $* tương đương với string "$1 $2 $3 ...$n"  (với $n là tham số cuối cùng khác null)

@

Nội dung của $@ sẽ là danh sách các tham số vị trí, bắt đầu từ vị trí 1. Tuy nhiên, danh sách đó có dạng "$1" "$2" ... ."$n". Khi không có tham số $@ có giá trị null.

$

Cung cấp ID của tiến trình của trình bao. Trong một trình bao con ( )  , $$ là ID của tiến trình của trình bao gọi không phải của trình bao con

!

Nội dung là tiến trình ID của lệnh sau cùng được thực thi trong nền (một cách không đồng bộ).

Quy ước về sử dụng dấu ngã ~ (tilde)

Trong BASH dấu ~ mang ý nghiã đặc biệt. tùy theo các kí tự nối theo sau nó.

  • ~   Giá trị của $HOME. Thí dụ: ~/mydir tương đương với thư mục $HOME/mydir
  • ~[Tên_tài_khoản]/ Trả về tên thư mục nhà (home directory) của [Tên_tài_khoản]
  • ~+  Tương đương với  gía trị $PWD
  • ~- Tương đương với  gía trị $OLDPWD

Hàm

Giống như C/C++Linux, BASH xem thủ tục cũng là mộtdạng hàm không có tham số. Cú pháp của một hàm thường là một trong các dạng

function <TÊN_HÀM> {
     <KHỐI_LỆNH>
}
<TÊN_HÀM> () {
     <KHỐI_LỆNH>
}
<TÊN_HÀM> ()
{  
     <KHỐI_LỆNH>
}

Đặc điểm chung của hàm trong BASH

Khi thưc thi một văn lệnh, có thể có trường hợp các mệnh lệnh, các lệnh nội tại, các tên thay thế (chẳng hạn như các tên làm ra từ lệnh alias) và các hàm có cùng tên, thì BASH sẽ ưu tiên tìm tới tên thay thế trước, sau đó là tên hàm, và sau cùng mới tìm kiếm trong các mệnh lệnh theo thứ tự này. Do đó, nên tránh đặt tên các hàm trùng với các tên thay thế. Ngoài ra, khi gọi các mệnh lệnh có cùng tên với hàm trong văn lệnh thì tốt nhất là dùng tên đầy đủ của mệnh lệnh đó

Hàm phải được định nghiã trước khi nó được sử dụng

Các biến thông thường của một hàm được chia sẽ chung trong một văn lệnh. Tuy nhiên, một biến địa phương có thể được tạo ra bởi từ khoá local (Thí dụ như local local_var=10)

Nếu sử dụng câu lệnh return trong một hàm thì nó sẽ lập tức trả dòng điều khiển về cho dòng điều khiển chính của văn lệnh và giá trị trả về (return) chính là giá trị thoát (exit status) mệnh lệnh cuối cùng mà hàm đó thực thi. Để chỉ thị một giá trị xác định khi trả về thì người ta dùng cú pháp return [n] (chẳng hạn như return 2 hay là return $number)

Một hàm có thể được xuất cho các trình bao con bằng cách dùng câu lệnh với cú pháp:

Oc|export -f |TÊN_HÀM}}

Các hàm được chứa trong một tập tin khác cùng có thể tải lên văn lệnh hiện hoạt bằng mệnh lệnh:

[TÊN_TẬP_TIN_HÀM]

hay là

source [TÊN_HÀM]

(Thí dụ: để sử dụng các hàm có định nghiã trong một tập tin là myFunc, thì người ta có thể tải nó trước bằng các thêm lệnh  . myFunc (hay lệnh source myFunc) vào trong văn lệnh hiện tại)

BASH hỗ trợ khả năng đệ quy của các hàm

Bên trong một hàm có thể gọi hàm khác. (xem Thí dụ)

Để xóa khỏi bộ nhớ hàm đã được tải từ trước, dùng lệnh có cú pháp: unset -f [TÊN_HÀM] Để hiển thị lại danh mục các hàm đã định nghiã từ trước dùng declare -f (xem lại bài 2) Thí dụ1:

#!/bin/bash
#script file name :hello
hello ()   #define this function before calling it
{
    echo "hello world"
    return 0      #optional you may add/remove this line
} 

echo "call function hello()" hello

Thí dụ2: Xuất các hàm sang nơi khác

Nội dung của tập tin file1:
#!bin/bash
#file name file1
#calling the function myfunc in the parent scripts (file2)
echo "calling from the child scripts"
myfunc
exit 0
Nội dung của tập tin file2:
#!/bin/bash
#file name file2
#Exporting function myfunc to all of its childs
myfunc ()
{
      echo "myfunc running"
}

#now call the child script to see what is done
# remember: chmod 755 ./file1
./file1

Truyền tham số và đọc gía trị trả về từ một hàm

Truyền tham số

Sử dụng các đối số theo cú pháp:

[TÊN_HÀM]  [TÊN_BIẾN1]  [TÊN_BIẾN2]  [TÊN_BIẾN3] ...

Với phương pháp này thì bên trong hàm thì nội dung $[TÊN_BIẾN1] sẽ được gán thành $1,  $[TÊN_BIẾN2] sẽ được gán thành $2, và $[TÊN_BIẾN3] sẽ được gán thành $3 ,....(xem thí dụ)

Sử dụng việc truyền tham số như là các biến thông thường. Tất cả các biến được định nghiã trong một văn lệnh sẽ được xem như là biến toàn cục của riêng văn lệnh đó (nếu chúng không được khai báo địa phương) và do đó chúng có thể được truy cập trực tiếp bên trong một hàm của văn lệnh này

Đọc giá trị trả về

Tiếp nhận giá trị nguyên trả về bằng lệnh return bên trong hàm (thí dụ2). Trường hợp này giá trị trả về chứa trong tham số đặc biệt #?

Tiếp nhận giá trị của các tham số thông qua các biến trong văn lệnh. Các biến trong một tập tin là biến toàn cục đối với tập tin đó, nên chúng có thể được thay đổi giá trị này bên trong hàm và được đọc lại từ bên ngoài hàm

Tiếp nhận các giá trị từ ngỏ ra của hàm. Trong trường hợp này hàm có thể dùng lệnh echo để xuất giá trị. (thí dụ3) Việc gọi hàm và giá trị tiếp nhận giá trị trả ra thông qua cú pháp :

$([TÊN_HÀM] [TÊN_BIẾN1]  [TÊN_BIẾN2]  [TÊN_BIẾN3] ...)

Thí dụ

Thí dụ1:  Truyền tham số bằng các biến thông thường

#!/bin/bash
myfunc ()
{
    echo " data passing before:  data=$dat, number=$num"
    data="new value"
   num=0
}
#now declaring variables
dat="orig value"
num=12

Thí dụ2: Nhận giá trị trả ra từ hàm.

Thí dụ tính tổng hai số nguyên từ hàm sum
#!/bin/bash
function sum {
     if [ ! "$#" = "2" ]; then
         echo "ERROR: the number of parameters must be 2"
         return
     else  
         local total
         let "total = $1 + $2"
         echo "is $total"
    if
}
value=$(sum 2 3)
echo "total of 2 and 3 $value"

Gọi các hàm đã được định nghĩa trong một tập tin khác

Như có nhắc trong phần đặc điểm, để việc chia sẽ mã nguồn được tiện lợi, BASH hỗ trợ một lệnh để tải một tập tin khác có chưá các thông tin hay các hàm và dùng nó để chia sẽ. Tập tin được tải có thể có mã nguồn trực tiếp sử dụng các biến hay tham số đã được định nghiã trong tập tin khai báo. Ngược lại, tập tin khai báo có thể gọi tất cả các hàm hỗ trợ sẵn có trong tập tin được tải.

Cú pháp dể khai báo tải một tập tin

[TÊN_TẬP_TIN_HÀM]

hay là

source [TÊN_TẬP_TIN_HÀM]

Thí dụ: Gọi hàm từ các tập tin ở nơi khác.

Nội dung của tập tin myFunc chứa định nghiã của các hàm, nó cần được tải và gọi bởi một văn lệnh khác

#!/bin/bash #file name myFunc func1 () { if [ $# -eq 1 ];then echo "the 1st parameter passed into func1 is $1" elif [ "$1" = "" ]; then echo "no parameter passed into func1" else echo "more than one parameters passed" if }

internal_Usage_Func () { echo "error : $*" 2>&1 echo "program $0 is terminated" exit 1 } func2 () { #recursive and internal function calling demo if [ ! "$#" = "1" ]; then internal_Usage_Func "func2() called from $0 require single argument" #in this case $0 will be the invoking script (mother) #so it not be Expect as myFunc else  local number=$1 # Variable "number" must be declared as local, # otherwise this doesn't work. if [ $number -eq 0 ]; then factorial=1 # Factorial of 0 = 1. else let "decrnum = number - 1" func2 $decrnum # Recursive function call. let "factorial = $number * $?" fi return $factorial fi }
Nội dung của tập tin chính bao gồm việc khai báo tải và gọi các hàm từ tệp myFunc

#!/bin/bash #file name mother . myFunc #declaring function will be loaded # equivalent declaring: source myFunc #now calling functions of file myFunc echo "func1 with 1 argument" func1 hi echo "func1 with 2 arguments" func1 hello world echo "---------" #testing func2 recursive function N=3 func2 $N echo "factorial of 3 is $?. This number is retrieved from func2" echo "now test the internal function call" func2 #no argument to trigger The output when invoking mother should look like: #func1 with 1 argument #the 1st parameter passed into func1 is hi #func1 with 2 arguments #more than one parameters passed #-------- #factorial of 3 is 6 #error : func2() called from mother require single argument #program mother is terminated"



Trở về mục lục

Đọc bài kế

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