9 .1.1
Phép toán lên một hạng tử
9.1.1.1 [ -b <Tên_Tập_Tin> ]
Trả về giá trị "true" nếu tập tin <Tên_Tập_Tin>
là một thiết bị kiểu khối (như là ổ mềm, cdrom, ổ cứng...) (Xem
thêm phụ lục)
9.1.1.2
[ -c <Tên_Tập_Tin> ]
Trả về giá trị "true" nếu tập tin <Tên_Tập_Tin>
là một thiết bị kiểu kí tự (như là bàn phím, modem, ...) (Xem
thêm phụ lục)
9.1.1.3
[
-d <Tên_Tập_Tin> ]
Trả về giá trị "true" nếu tập tin <Tên_Tập_Tin>
là một thư mục
Thí dụ:
if [ -d /home/Bill ]; then
echo "directory /home/Bill exist"
if
9.1.1.4
[ -e <Tên_Tập_Tin> ]
Trả về giá trị "true" nếu tập tin <Tên_Tập_Tin>
tồn tại
Thí dụ:
if [ -e myfile ]; then
echo "my file exists"
fi
9.1.1.5 [ -f <Tên_Tập_Tin>
]
Trả về giá trị "true" nếu tập tin <Tên_Tập_Tin>
là tập tin thông thường
9.1.1.14
[ -g <Tên_Tập_Tin>
]
Trả về giá trị "true" nếu bit setgid cài lên đó (Xem
thêm phụ lục)
9.1.1.7
[ -k <Tên_Tập_Tin> ]
Trả về giá trị "true" nếu bit Sticky được cài lên tập tin (Xem
thêm phụ lục )
9.1.1.8 [ -O <Tên_Tập_Tin>
]Trả về giá
trị "true" nếu bạn là chủ tập tin <Tên_Tập_Tin>
(Xem thêm phụ
lục )
9.1.1.9
[ -p <Tên_Tập_Tin>
] Trả về
giá trị "true" nếu tập tin <Tên_Tập_Tin>
có kiểu ống dẫn truyền (Xem
thêm phụ lục)
9.1.1.10 [ -L <Tên_Tập_Tin>
] Trả về
giá trị "true" nếu tập tin <Tên_Tập_Tin>
là liên kết mềm (Xem
thêm phụ lục)
9.1.1.11
[ -S <Tên_Tập_Tin>
] Trả về
giá trị "true" nếu tập tin <Tên_Tập_Tin>
là tập tin ổ nối (Xem
thêm phụ lục)
9.1.1.12 [ -r <Tên_Tập_Tin> ]
Trả về giá trị "true" nếu tập tin <Tên_Tập_Tin>
có thuộc tính đọc được
(Xem thêm phụ
lục)
9.1.1.13
[
-s <Tên_Tập_Tin>
] Trả về giá trị "true" nếu tập
tin <Tên_Tập_Tin> có độ dài lớn hơn 0 byte
9.1.1.14
[ -u <Tên_Tập_Tin>
]
Trả về giá trị "true" nếu bit setuid cài lên đó (Xem
thêm phụ lục)
9.1.1.15
[ -w <Tên_Tập_Tin> ]
Trả về giá trị "true" nếu tập tin <Tên_Tập_Tin>
có thuộc tính cho phép viết
(Xem thêm phụ
lục)
9.1.1.16
[ -x <Tên_Tập_Tin>
]
Trả về giá trị "true" nếu tập tin <Tên_Tập_Tin>
là tập tin thực thi được (Xem
thêm phụ lục )
9.1.2 Phép toán lên hai hạng
tử
9.1.2.1
[
<Tên_Tập_Tin_1>
-nt <Tên_Tập_Tin_2> ] Trả về giá trị "true" nếu tập
tin
<Tên_Tập_Tin_1>
mới hơn tập tin <Tên_Tập_Tin_2>
9.1.2.2
[
<Tên_Tập_Tin_1>
-ot <Tên_Tập_Tin_2>
]
Trả về giá trị "true" nếu tập tin
<Tên_Tập_Tin_1>
cũ hơn tập tin <Tên_Tập_Tin_2>
9.1.2.3
[
<Tên_Tập_Tin_1>
-ef <Tên_Tập_Tin_2>
]
Trả về giá trị "true" nếu tập tin
<Tên_Tập_Tin_1>
và <Tên_Tập_Tin_2>
cùng liên két tới một tập tin .
Lưu ý:
Nếu
<Tên_Tập_Tin_1>
và <Tên_Tập_Tin_2>
là hai loại tập tin liên kết khác nhau thì
phép toán luôn luôn trả về giá trị "false" (một tệp là liên kết mềm còn
tệp kia là liên kết cứng)
9.2 Phép toán so sánh trên các số nguyên
9.2.1 Toán tử
-eq Trả về giá trị "true" nếu cả hai hạng
tử bằng nhau. Thí dụ:
[ $a -eq 5 ]
9.2.2 Toán tử
-ne
Trả về giá trị "true" nếu cả hai hạng tử không bằng nhau. Thí dụ:
[ $a -ne $b ]
9.2.3 Toán tử
-gt
Trả về giá trị "true" nếu hạng tử thứ nhất lớn hơn hạng tử thứ hai
9.2.4 Toán tử
-lt Trả về giá trị "true" nếu hạng
tử thứ nhất nhỏ hơn hạng tử thứ hai
9.2.5 Toán tử
-le
Trả về giá trị "true" nếu hạng tử thứ nhất nhỏ hơn hay bằng hạng tử thứ hai
9.2.6 Toán tử
-ge
Trả về giá trị "true" nếu hạng tử thứ nhất lớn hơn hay bằng hạng tử thứ hai
9.3 Phép toán so sánh trên các chuỗi kí tự
9.3.1 Toán tử
=
Trả về giá trị "true" nếu cả hai hạng tử bằng nhau. (Thí dụ:
[ $a = "mystring"
] )
9.3.2 Toán tử
!=
Trả về giá trị "true" nếu cả hai hạng tử không bằng nhau. (Thí
dụ: [ $a != $b ] )
9.3.3 Toán tử
\<
Trả về giá trị "true" nếu hạng tử thứ nhất nhỏ hơn hạng tử thứ hai
theo bảng thứ tự ASCII (Thí dụ: [ $a \< $b
] )
9.3.4 Toán tử
\>
Trả về giá trị "true" nếu hạng tử thứ nhất lớn hơn hạng tử thứ hai
theo bảng thứ tự ASCII ASCII order
9.3.5 Toán tử
-z
Trả về giá trị "true" nếu hạng tử duy
nhất này có độ dài bằng 0 ( Thí dụ: if [ -z $mystr ]; then ...;
fi)
9.3.6 Toán tử
-n
Trả về giá trị "true" nếu
hạng tử duy nhất này có độ dài lớn hơn 0
9.4 Phép toán logic trên các mệnh đề con
9.4.1 Toán tử
-a: phép logic AND
Thí dụ:
if [ $x -eq 10 -a $y -gt 1 ]; then
echo "If x equals 10 AND y greater than 1 then"
fi
9.4.2 Toán tử
-o:
phép logic OR
Thí dụ:
if [ $x -eq 10 -o $y -gt 1 ]; then
echo "If x equals 10 OR y greater than 1 then"
fi
9.5 Phép toán logic trên các biểu thức
Có hai toán tử quan trọng đuợc dùng để nối giữa các biểu thức, đó là || (tương
đương với toán tử logicl OR), && (tương
đương với toán tử
logic AND) và ! (tương đương với toán
tử NOT). Các phép toán này thường dùng trong các mệnh đề điều kiện như
là if, while, for, ...
Thí dụ:
echo -n "Erase this file (y/n) :? "
read ANSWER
if [ "$ANSWER" = "y" ] || [ "$ANSWER" = "yes" ] ; then
rm -f $file
elif [ "$ANSWER" = "n" ] || [ "$ANSWER" = "no" ] ; then
echo "user abort the operation"
exit 0
else
echo "Wrong keyed!"
fi
Thí dụ2:
while [ $A -lt 8 ] && [ ! "$B" = "n" ]; do #while A
less than 4 and B is not equal "n" do
let "A +=2"
#increasing A (see next paragraph)
echo "stop with A=$A (y/n)? "
#asking for stop
read B
#receiving user's decision in B
done
if [ $A -eq 8 ]; then
echo "A reachs maximum value $A"
fi
10 Phép toán trên các biến
10.1 Phép toán lên chuỗi kí tự
Các phép toán trên chuỗi kí tự đã được trình bày chi
tiết trong
bài 2 phần 6, 7 và 8
10.2 Phép toán số học trên biến nguyên
Để áp dụng các phép toán số học như +, -, * / .. lên các biến có giá trị nguyên
thì có thể dùng lệnh
let "<Mệnh_đề_phép_toán_số_học>".
Tương đương như thế, , cũng có thể dùng
((<Mệnh_đề_phép_toán_số_học>))
để thực thi các phép toán. Nếu dùng
let hay
(( )) để gán giá trị nguyên
lên biến thì có thể đặt các kí tự trống (space) ở giữa toán tử
= mà không sợ bị
vi phạm lỗi cú pháp (xem lại cách
gán giá trị
đơn giản). Ngoài ra, sau khi thi hành các phép tóan số học,
let hay
(( )) sẽ trả
về giá trị 0 nếu giá trị của
<Mệnh_đề_phép_toán_số_học>
khác không và trả về giá trị 1 cho các trường hợp còn lại.
10.2.1.
Phép gán giá trị
=
Thí du:
var1=0
var2=1
let "var1 = $var2" # remember the assigned variable has NO '$'
sign
let "var2= 4"
echo "var1=$var1 and var2=$var2"
((var2 += var2))
echo "var2"
10.2
.2. Cộng
+
Thí dụ: let "var1 = $var2 + 2 + $var1"
10.2.3. Trừ
-
10.2.4. Nhân
*
10.2.5. Chia
/
10.2.6. Lũy thưà
**
10.2.7. Modulo
%
Cạnh đó, BASH cũng hỗ trợ một số phép toán
tương tự như trong ngôn ngữ C/C++:
10.2.8
Plus-equal +=
:
Thí dụ:
let "var1 +=5"
# it equivalent to the command: let "var1 =
$var1 +5" or command ((var1 += 5))
10.2.9 Minus-equal
-=
10.2.10 Multiply-equal
*=
10.2.11 Divide-equal
/=
10.2.12 Modulo-equal
%=
10.3 Phép toán nhị phân (Binary
oparation) trên các biến nguyên
10.3.1. shift left
<<
Đẩy các bit sang trái 1 bit (tương đương
với nhân cho 2)
10.3.2. shift left-equal
<<=<n>
Đẩy các bit sang trái <n> bit
Thí dụ:
let "myvar <<= 2"
#results in myvar is left-shifted 2 bits (or multiplied by 4)
10.3.3.
Phép toán nhị phân
shift right
>>
Đẩy sang phải 1 bit
10.3.4. Phép toán nhị phân shift right-equal
>>=<n>
10.3.5. Phép toán nhị phân AND
&
10.3.6. Phép
toán nhị phân AND-equal
&=
10.3.7. Phép
toán nhị phân OR
|
10.3.8. Phép
toán nhị phân OR-equal
|=
10.3.9.
Phép toán nhị phân nghịch đảo ~
10.3.10. Phép toán nhị phân NOT
!
10.3.11. Phép toán
nhị phân XOR
^
10.3.12. Phép toán nhị phân XOR-equal
^=
10.3 Phép toán điểm chấm động (floating point) trên các biếnBASH không hỗ trợ cho các tính toán điểm chấm động
(floating point). Tuy nhiên, lệnh
bc
có thể giúp tiến hành các loại toán này. Cú phép đơn giản để tiến
hành là:
<TÊN_BIẾN>=$(echo
"<Tham_Số>";
<Biểu_Thức_Số_Học>" | bc
[<Tham_Số_Ngắn>])
Giá trị của <Tham_Số>
Có thể là:
-
length số chữ số có
nghiã trong biểu thức
-
scale
số chữ số thập phân sau dấu chấm trong biểu thức
-
sqrt
(hay
read).
Giá trị của
<Tham_Số_Ngắn>
Có thể là:
-
-l
cho thư viện toán,
- Các
tham số khác như là
-i,-q,-s. (xem thêm chi
tiết bằng lệnh hướn dẫn man bc)
Khi dùng Tham số ngắn -l thì mệnh lệnh sẽ load từ trước thư viện toán bao
gồm cả các hàm
s(x)
--sine,
c(x)
-- cosine,
a(x)
-- arctan, l(x) logarith tự nhiên,
e(x) -- mũ ơ số e, và
j(n,x) -- Hàm Bessel của số nguyên n của x.
Lưu ý:
lệnh bc hỗ trợ nhiều tính toán có khả năng lập trình được với nhiều tính
năng khác bao gồm cả biến số, vòng lặp và phân nhánh. Xem
thêm chi tiết bằng lệnh man bc
Thí dụ1:
#this example calculates Pi number with
10 digit of accuracy
Pi = $(echo "scale=10; 4*a(1)" | bc -l) # using
arctan(1) to calculate Pi/4
echo "PI=$Pi" #it should be 3.1415926532
Thí dụ2:
# this example call bc without the
short_option -l
radius=1.234
Pi=3.1416
$result=$(echo "scale=4; $radius*$radius*$pi" |bc) # we
need only 4 digits
echo "the area occupied by this circle is :$result"
10.4 Dùng
expr
để thực thi các phép toán
Đây là lệnh đánh giá một mệnh đề cuả BASH. Nó trả về giá
trị trong các phép toán số học (trả về giá trị nguyên), các so sánh (trả về 1
hay 0), các phép toán lên chuỗi kí tự. Ta quy ước gọi ARG, ARG1
và
ARG2
là các tham số trong biểu thức cần được đánh giá. (tham số này sẽ là giá trị của
một biến (nếu biến đó là
myvar thì
ARG
phải viết thành
$myvar), của
một string (các string phải để trong ngoặc "
hay '
) , hay của một hằng
Lưu ý
: Lệnh
expr
có thể không hoạt động hữu hiệu trong
một số cài đặt mặc định cho X Windows (chẳng hạn như chương trình "Terminal
Emulation - trình đơn Shell Console của SUSE SLES 9. Để chạy được lệnh
expr phải dùng trình đơn Linux Console trong X Window). Muốn cho lệnh này hoạt
động đúng chức năng, tốt nhất là chọn chế
độ chạy trên đầu cuối trực tiếp
10.4.1 Phép toán số học:
Dùng cú pháp expr ARG1
<Phép_Toán>
ARG2
10.4.2 Phép so sánh: Trả
về 1 nếu mệnh đề đúng và 0 nếu sai.
ARG1 và
ARG2 của nó có thể là
giá trị số nếu là phép so sánh hai số hay giá trị chuỗi kí tự nếu là phép so
sánh hai string. Các phép toán bao gồm
- =
Kiểm nghiệm sự bằng nhau của hai hạng tử
- \>
Kiểm nghiệm sự lớn hơn của hạng tử bên trái
- \<
Kiểm nghiệm sự nhỏ hơn của hạng tử bên trái
- \>=
Kiểm nghiệm sự lớn hơn hoặc bằng hơn của hạng tử bên trái
- \<=
Kiểm nghiệm sự nhỏ hơn hoặc bằng hơn của hạng tử bên trái
Cú pháp chung là expr ARG2
<Phép_So_Sánh>
ARG1
10.4.3 Phép toán lên chuỗi kí tự:
Ngoài phép so sánh và phép tính số học, expr cung cấp thêm một số phép toán
trên các string: ARG lúc này phải có giá trị của một string (đặt chúng trong
ngoặc ' hay " ). Để thực hiện thì đòi hỏi dùng thêm các từ khoá như match, substr, index, và
length
- expr substr
ARG <Vi_Trí>
<Độ_Dài>
trả về một chuỗi kí tự con của
ARG
bắt đầu từ vị trí
<Vi_Trí>
và có độ dài là
<Độ_Dài>.
Nếu
<Độ_Dài>
lớn hơn độ dài còn lại của
ARG
thì nó chỉ trả về phần còn lại của
ARG
- expr index
ARG string : Trả về chỉ số (hay vị trí) của
string trong ARG nếu
nó được tìm thấy
- expr length
ARG : Trả về độ dài của string
ARG
Lưu ý: Ngoài ra,
expr
còn cung cấp một số phép toán khác như là phép
logic hoặc |,
logic và
&, và phép tương hợp với một dạng thức
expr
match ARG RE ; trong đó, RE là một
biểu thức chính
quy (một cú pháp tương đương là
expr ARG:RE
). Sẽ trả về 0 nếu
ARG
không tương hợp với
RE; ngược
lại sẽ là giá trị khác 0 thường là độ dài của RE. Tuy
nhiên, trong nhiều trường hợp lệnh expr này
không tương thích được hoàn toàn với hệ điều hành và trở nên khó dùng nên
tránh và có thể thay bằng các lệnh khác chẳng hạn như dùng các phần đa trình
bài trong bài 2
phần 6,
7, và 8
10.4.4 Thí dụ:
phép modulo
expr 8 % 3
# 2
Phép cộng
y=5
y=`expr $y + 1`
echo "\$y=$y"
# 6
x=10
sum=`expr $x + $y`
echo "\$sum=$sum"
# 16
Phép so sánh logic
x=2
y=4
res=`expr $x = $y`
echo "evaluation of expression = retunrs $res"
# 0
res=`expr $x \< $y`
echo "evaluation expression < ruturn $res"
# 1
Phép toán trên các string
mystr="abcd 12345abcddd"
#so sánh
str="2345"
if [ `expr $mystr = $str` = 0 ]; then
echo "comparing of $mystring and $str returns false (0)"
else
echo "comparing of $mystring and $str returns true (1)"
fi
#trả về chuỗi kí tự con
res=`expr substr "$mystr" 3 4`
echo "string at position 3 with the length of 4 from $mystr is: $res"
# "cd 1"
#trả về chỉ số của chuỗi kí tự con nếu tìm thấy
res=`expr index $mystr $str`
# 5
10.5 Dùng cơ số trên các biến số
Trong mặc định thì BASH thông dịch các giá trị số trong cơ số thập
phân. Có thể dùng lệnh
let
để sử dụng giá trị một số cơ số không thập phân. Thí dụ sau đây sẽ
chỉ ra cách tiến hành:
Thí dụ:
#Decimal
let "d=12"
echo "value of d is $d" #it is 12
#Octal
let "o = 012" #using number zero (0) as prefix
for octal
echo "o =$o" # it is now 12
(octal)=> 10
#Hexadecimal
let "h = Ox1c" #using '0x' as prefix for hexadecimal
echo "h = $h" # it is now 1c (hex) => 28
#other base between 2 and 64
let "b = 32#15 #format as BASE#VALUE
echo "base 32 predefined value of b is $b" #result 40
11 Tham chiếu gián
tiếp tới biến:
Trong thực tiễn, đôi khi người lập
trình muốn truy đọc gía trị của một biến mà tên của biến đó lại là
nội dung giá trị của một biến khác. Tức là nếu
myVar
được mang giá trị "myIndirectVar",
và nếu lại có một biến mang tên
myIndirectVar
chứa giá trị là "XYZ", thì
người ta có thể hoặc là dùng lệnh
eval myNewVar=\$$myVar
hay
myNewVar=${!myVar}
để gán giá trị "XYZ" cho biến
myNewVar
11.1
eval
<TÊN_BIẾN>=\$$<TÊN_BIẾN_THAM_CHIẾU>:
Lệnh này không cho phép xuất (output) trực tiếp giá trị thành một
chuỗi kí tự nhưng cho phép gán giá trị (chuỗi kí tự) đó lên một
biến khác
Thí dụ:
myVar="Indirect_Var"
#MyVar is assigned a value as "Indirect_Var"
Indirect_Var="Here is the 1st value"
#The Indirect_Var has bên declared and contained the value of "Here is the 1st value"
eval MSG=\$$myVar
echo "here is: $MSG"
Indirect_Var="Here is the 2nd value"
#reassign a new value for Indirect_Var,
#but it cannot be directly
output
#and must use eval command to get it
eval MSG=\$$myVar
echo "here is after changing: $MSG"
11.2
<TÊN_BIẾN>=${!<TÊN_BIẾN_THAM_CHIẾU>}:
Lệnh này cũng làm cùng một thao tác nhưng có thể cho phép hiển thị
trực tiếp giá trị chuỗi kí tự mà nó tham chiếu gián tiếp tới
Thí
dụ:
myVar=An_Indirect_Var
#MyVar is assigned a value as "An_Indirect_Var"
An_Indirect_Var="Here is the 1st value"
#An_Indirect_Var has a value as "Here is the 1st value"
echo "here is: ${!myVar}"
An_Indirect_Var="Here is the 2nd value" #you cannot directly
output
echo "here is after changing: ${!myVar}"
12 Các câu lệnh phân
nhánh12
.1 Câu lệnh
If: Như mọi ngôn ngữ lập
trình, biểu thức if
có thể dùng để tạo sự rẽ nhánh.
Lưu ý rằng trong BASH mỗi mệnh đề
if
luôn luôn phải có kết thúc bằng từ khoá
fi
12.1.1: Câu lệnh
if
đơn giản
: Dùng cú pháp sau đây
if [
<Biểu
thức_Điều_Kiện> ]; then
<Khối_Lệnh>
fi
Hay là :
if [
<Biểu
thức_Điều_Kiện> ]
then
<Khối_Lệnh>
fi
Hay là :
if [
<Biểu
thức_Điều_Kiện> ];
then
<Khối_Lệnh>;
fi
Hay là bất kì dạng tổ
hợp nào khi thay thế mỗi kí tự đầu dòng bằng một kí tự
;
Lưu ý:
Như một sự nhắc nhớ: các khoảng trống (space) giữa dấu ngoặc vuông và
biểu thức điều kiện cần phải được giữ đúng nếu không muốn bị trình dịch bắt
lỗi cú pháp
12.1.2
Câu lệnh
phức hợp
if [ <Biểu
thức_Điều_Kiện1> ];then
<Khối_Lệnh1>
elif [ <Biểu
thức_Điều_Kiện2> ]
<Khối_Lệnh2>
elif [ <Biểu
thức_Điều_Kiện3> ]
<Khối_Lệnh3>
else
<Khối_Lệnh4>
fi
Từ khoá
elif
có thể dùng để "kéo dài" số trường hợp phân nhánh ra hoặc rút ngắn lại thành
câu lệnh if-else có cú pháp như sau
if [
<Biểu
thức_Điều_Kiện> ];then
<Khối_Lệnh1>
else
<Khối_Lệnh2>
fi
Lưu ý: Như đã biết, thay vì viết từ khoá then trong
một hàng mới, bạn luôn luôn có thể thay kí tự xuống hàng bằng ; thành
;then
và
giữ từ khoá này trong cùng một hàng. Tương tự cho từ khoá "do" trong các
vòng lặp. Thí dụ:
if [
$?=0 ]; then echo "no error found"; else echo "found error: $?"; fi.
Và điều này đúng cho bất kì tổ hợp câu
lệnh nào trong BASH
Thí dụ: Đoạn mã sau đây tiến hành lệnh
cp (chép
tập tin file1 sang thành
file2) và sau đó kiểm tra xem kết quả của lệnh
cp (có
trả về lỗi Khác không nào hay không). Nếu có lỗi tiến hành lệnh
cp, thì
kiểm xem tồn tại LOG file thì chép nối vào log file. Còn không có
LOG
file thì chỉ hiển thị ra màn hình
LOG="mylog.txt"
....
cp file1 file2
if [ $? = 0 ]; then
echo "suceed!"
elif [ -e "$LOG" ]; then
echo "failed" >> "$LOG"
else
echo "copy failed"
fi
12.
2
Phân nhánh dùng lệnh case:
Tương tự, câu lệnh case cho phép phân làm
nhiều nhánh cùng lúc thay vì phải dùng tổ hợp
if-else
case
<TÊN_BIẾN> in
<Giá_Trị1>)
<Khối_Lệnh_1>
;;
<Giá_Trị2>)
<Khối_Lệnh_2>
;;
...
*)
#default
<Khối_Lệnh_Mặc_Định>
;;
esac
Thí
dụ:
#this program can run only you
have X-window;
#the example indicated that it is compatible with
wild card
characters.
echo -n "chose font color for xterm in
x-window: "
read color
case "$color" in
[Bb]l??)
xterm -fg blue &
;;
gree*)
xterm -fg darkgreen &
;;
red | orange)
#red or orange
xterm -fg "$color" &
*)
xterm -fn terminal &
;;
esac
Lưu ý:
trường hợp không muốn xác định giá trị cụ thể của
<TÊN_BIẾN>
mà vẩn muốn thi hành tiếp các lệnh điều khiển thì có thề dùng
trường hợp *)
như là sự mặc định giá trị
của biến không thoả mãn bất kì trường hợp thử nào thì sẽ thi hành
<Khối_Lệnh_Mặc_Định>
đó. Tuy nhiên, cũng
không nhất thiết phải có sử dụng trường hơp
*)
này trong mỗi câu lệnh
case.
13 Các câu lệnh vòng lặp:Khi có một khối lệnh cần được thực thi nhiều lần,
mỗi lần chỉ khác nhau một vài giá trị gán ban đầu của các biến thì người ta có
thể dùng kĩ thuật vòng lặp. Những biến có chỉ thị để thay đổi giá trị cài đặt
ban đầu cho mỗi lần thực thi khối lệnh của vòng lặp gọi là
biến chỉ số (index variable). Tập họp các
giá trị được lần lược gán lên biến chỉ số X theo thứ tự được gọi là
danh sách giá trị của biến X (hay ngắn gọn hơn
là <Danh_Sách> )
Bash hỗ trợ 4 loại câu lệnh cho vòng lặp là
for, while, until ,
và
select
.
Trước khi
vào đề thì một trong những lưu ý quan trọng nhưng rất cổ điển là khi dùng vòng
lặp phải nhớ hai điều:
-
Vòng lặp phải có lối thoát nếu không
muốn tạo ra tình trạng treo chương trình , treo máy.
-
Các điều
kiện cực biên phải chính xác. Nếu điều kiện biên sai sót sẽ dẫn đến tình
trạng chương trình có hỏng hóc khó sửa vì sự sai sót chỉ xãy ra ỏ các giá trị
cực biên và thông thường chương trình tưởng chừng chạy hoàn hảo cho tới khi ....
một trong các biến của vòng lặp đạt giá trị cực biên.
Bên cạnh đó, còn có các lệnh chuyên dùng
trong các vòng lặp để điều chỉnh hướng thực thi
13.1 Điều chỉnh vòng lặp: break
và
continue
:
Nhiều mệnh lệnh như là
break,
continue, exit,
exec, ... có thể làm đổi hướng hay ngưng lập tức
sự vận hành của các vòng lặp. Trong phần này chúng ta chỉ giới thiệu
sơ lược. Phần chi tiết sẽ bàn tới trong 13.7. Các thí dụ về chúng
cũng sẽ được trình bày trong phần kế tiếp
13.1.1 break:
Lệnh này sẽ chấm dứt vòng lặp nhỏ nhất chứa nó ngưng ngay lập tức và tiếp
tục chạy các mã tiếp theo
13.1.2
continue:
Lệnh này sẽ buộc ngưng thi hành phần còn lại của mã và bắt đầu vòng
chu kì mới tiếp theo của vòng lặp
13.1.3 Ngoài ra vòng lặp còn có thể
ngưng một khi lệnh
exit, exec
(hay các lệnh có tính kết thúc tiến trình hiện tại để thi hành việc khác
hay để trả về hệ điều hành)
13.2 Cách
thành lập
<Danh_Sách>
cho một vòng lặp
Như trong vòng lặp for của C/C++, trong
1 vòng lặp thì danh
sách có thể là các số nguyên tăng (hay giảm) một
hằng số nguyên và cách thành lập này hoàn toàn tương tự. Bạn có thể
xem lại cú pháp cho lệnh
for.
Ngoài ra, BASH còn hỗ trơ các hình thức tạo lập danh
sách khác dùng trong lệnh
for và lệnh
select như sau
13.1.1 Dùng mặc định
Theo mặc định thì một
<Danh_Sách>
dùng trong vòng lặp for có thể là một trong các dạng:
13.1.1.1
Các tên chuỗi kí tự phân cách bởi khoảng trống (space)
Thí dụ1:
for FRUIT in 'apple' 'apricot' 'lemon' 'plum' 'orange'; do
if [ "$FRUIT" = "lemon" ]; then
echo "lemon found. Using
'continue' key word to jump over this record"
continue
if
echo "The fruit is $FRUIT"
done
13.1.1.2
Danh sách các số
phân cách bởi khoảng trống (space)
Thí dụ2:
for i in 1 2 3 4; do
echo "$i"
done
13.1.1.3 Danh sách các tên như thí dụ 1,2,3 nhưng phân
cách bởi kí tự xuống hàng
Thí dụ3:
LIST="/root/file1
/etc/file2
/var/spool/file3"
for FILE in $LIST; do
if [ -e $FILE ]; then
echo "file $FILE exist"
else
echo "file $FILE not
exist. Stop any loop by 'break' command"
break
fi
done
13.1.1.4 Danh sách có thể cho được từ
các tên dùng
kí tự
phỏng địnhThí dụ4:
for FILE in [abc]*; do
rm -f $FILE
done
#this script will remove all files whose name beginning by
character a, b, or c in current directory
13.1.1.5 Danh sách có thể cho được từ các dòng hiển
thị của một khối lệnh (hay một mệnh lệnh):
Thí dụ5:
for file in $( find $directory -type
l ) # -type l = symbolic links
do
echo "$file"
done | sort
#this for loop will search all soft link file
and output after sort them
13.1.2 Ngăn cách giữa
các miền cho một
<Danh_Sách>:
Thật ra, danh sách các tên tạo ra mà chúng ngăn cách nhau một cách
mặc định đã được quy định trước bởi giá trị của biến ngăn cách
(separator) IFS trong BASH. Đó là các giá trị kí tự khoảng
trống, kí tự nhảy bước, hay kí tự xuống hàng
(space,tab,newline
). Chúng ta hoàn
toàn có thể cài đặt lại giá trị này
(chứa trong
$IFS ) để sử dụng và trả lại giá trị
ban đầu sau khi dùng xong. Thí dụ sau đây minh hoạ cho thao tác này:
Thí dụ:
# file runnit
#!/bin/bash
fruits=plum:orange:grape:banana
#the 'fruit' list using ':' as separate character
old="$IFS" #save the current shell separator
IFS=":" #reassign the new ':'
separator
for FRUIT in $fruits
do
echo "this is $FRUIT
done
IFS="$old" #returning original value
13.3
for
: Biến chỉ số
<TÊN_BIẾN>
sẽ lần lượt lấy các giá trị trong <Danh_Sách> và thi hành theo thứ tự trừ khi bị ngắt ngang bởi các lệnh điều
chỉnh vòng lặp như
break, continue ,
hay
exit,
exec. Sau đây là một số cú pháp tiêu biểu
<TÊN_BIẾN> in
<Danh_Sách>; do
<KHỐI_LỆNH>
done
for
<TÊN_BIẾN>
in
<Danh_Sách>
do
<KHỐI_LỆNH>
done
for ((a=MIN; a <= MAX; a++))
do
<KHỐI_LỆNH>
done
for ((a=MIN1; b=MIN2; a<MAX; a++; b++))
do
<KHỐI_LỆNH>
donefor ((
<Biểu_Thức1>;
<Biểu_Thức2>;
<Biểu_Thức3>
)) ; do
<KHỐI_LỆNH>
; done Trong đó,
<Biểu_Thức1>
sẽ được đánh giá sau đó là việc đánh giá của biểu
<Biểu_Thức2>
và mỗi lần
<Biểu_Thức2>
có giá trị khác 0, thì khối lệnh sẽ được thi hành và sau
đó sẽ
là việc đánh giá
<Biểu_Thức3>.
Nếu bất kì biểu thức nào không có mặt thì chỗ thiếu vắng đó sẽ được
xem như là một biểu thức luôn luôn có giá trị 1. Giá trị trả về của
vòng lặp này là trạng thái thoát (exit status) của mệnh lệnh sau cùng
trong khối lệnh, hay trả về giá trị "false"
nếu các biểu thức là không hợp lệ.
Như là một bài tập,
hãy thử nghiệm các vòng lặp for với
biến chỉ số giảm dần thay vì tăng như trong cú pháp nêu trên; đồng thời hãy
thử dùng giá trị cực tiểu (MIN) ỏ trên là số âm để xem khả năng hỗ trợ của
BASH.
Lưu
ý: Việc trình bày bất kì một khối lệnh hay câu lệnh nào trong BASH đều có
thể thay các kí tự xuống hàng bởi các dấu
;
Thí dụ1:
for fruit in "red apple" "yellow orange" "green grape"
do
echo "color fruit : $fruit"
done
Thí dụ2:
for CHAR in `cat $myFile.txt`; do
echo "$CHAR"
done
Xem thêm thí dụ về vòng lặp
for
trong phần nói về danh sách
13.4
while
:
Biểu thức
<Biểu
thức_Điều_Kiện>
sẽ được kiểm nghiệm
cho tới khi nó không còn được thoả mãn thì vòng lặp sẽ bị ngừng .
Các cú pháp tương đương hay thấy là:
- while [
<Biểu
thức_Điều_Kiện> ]; do
<KHỐI_LỆNH>
done
while [
<Biểu
thức_Điều_Kiện> ]
do
<KHỐI_LỆNH>
done
while [
<Biểu
thức_Điều_Kiện> ];
do
<KHỐI_LỆNH>;
done
Thí dụ1:
var=0
MAX=10
while [ "$var" -lt "$MAX" ]
do
echo -n "$var "
# -n suppresses new-line.
var=$(($var+1))
done
#revise the above while statement using 'break'
keyword
var=0
MAX=10
while (true); do
echo -n "$var "
# -n suppresses new line.
var=$(($var+1))
if [ $var -eq 10 ]; then
break
fi
done
Thí dụ2:
#Example of multiple condition line of while statement
#!/bin/bash
var=init
pre=$var
while echo "previous variable =
$pre"
echo
pre=$var
[ "$var" != "q" ]
# Four conditions on "while", but only
the last one controls loop.
do
echo
-n "Input variable (enter
'q' to exit) :"
read var
echo "variable = $var"
done
Thí dụ3:
#example while using C style
#BASH STYLE:"
LIMIT=10
a=1
while [ "$a" -le $LIMIT ]
do
echo -n "$a "
let "a+=1"
done #
No surprises, so far.
echo; echo
# Now, with C style.
((a = 1)) # a=1
# Double parentheses permit space when setting a variable, as in
C.
while (( a <= LIMIT )) # Double parentheses, and no
"$" preceding variables.
do
echo -n "$a "
((a += 1)) # let "a+=1"
# Double parentheses permit incrementing a variable
done
13.5
until:
tương tự như vòng lặp
while
nhưng
<Biểu
thức_Điều_Kiện>
được kiểm nghiệm trước cho tới khi giá trị của nó là "true" thì vòng
lặp sẽ bị ngừng
- until [
<Biểu
thức_Điều_Kiện> ]; do
<KHỐI_LỆNH>
done
until [
<Biểu
thức_Điều_Kiện> ]
do
<KHỐI_LỆNH>
done
until [
<Biểu
thức_Điều_Kiện> ];
do
<KHỐI_LỆNH>;
done
Thí dụ:
#!/bin/bash
var=
until [ "$var" = "q" ] # Tests condition
here, at top of loop.
do
echo -n
"Input variable (enter 'q' to exit :"
read var
echo "variable = $var"
done
13.6
select:
Đây là dạng vòng lặp mô phỏng của
trình bao Korn. Nó có ích để tạo một trình đơn (menu) lựa chọn
từ một danh sách
của tên mà biết chỉ số sẽ lần lược được gán.
- PS3="<Dòng_Thông_Báo>"
# it will be
prompted in the 'select' statement
...
select
<TÊN_BIẾN> in
[
<Danh_Sách>
]
do
<KHỐI_LỆNH>
break;
done
Thí dụ:
#!/bin/bash
PS3='Choose your favorite fruit: ' #
Sets the prompt string.
#this is default variable of 'select' statement
echo
select FRUIT in "orange"
"grape" "banana"
"plum" "mango"
do
echo "Your
fruit is $FRUIT."
echo "Yummy!"
echo
break #
without 'break'
the loop will be
infinity
done
13.7
Vòng lặp lồng nhau (net loop) và điều chỉnh
vòng lặp: Để tránh lỗi
mập mờ (ambiguity), lệnh
break
và continue
trong các vòng lặp lồng nhau có hỗ trợ
về cấp độ của vòng lặp chịu ảnh hưởng
bởi sự thi hành của nó.
-
break
<n>
: Với
<n>
là cấp độ của vòng
lặp sẽ bị điều chỉnh bởi lệnh
break .
Trong đó,
break
1 (hay viết đơn
giản break )
sẽ chỉ ảnh hưởng đến vòng lặp trong cùng nhất có chứa từ khoá
break .
thí dụ
break 2 sẽ chỉ có
ảnh hưởng tới vòng lặp kế bao ngoài vòng lặp trong cùng (cấp 2)
-
continue
<n> :
Hoàn toàn tương tự
continue n
sẽ có hiệu lực đối với vòng lặp cấp n
Thí dụ:
#!/bin/bash
#name nestdemo
for month in jan feb mar apr may jun jul aug sep oct nov dec; do
for week in 1 2 3 4; do
echo -n "Start processing the month
of $month. (y/n)? :"
read ans
if [ "$ans" = "n" ] || [ -z "$ans" ];
then
continue 2
#skip
the rest go back to next value of $month
# from
the outer loop
else
echo -n
"Start processing week $week of the month $month (y/n)?"
read
ans
if [
"$ans" = "n" ] || [ -z "$ans" ]; then
continue
#skip the this inner most loop
else
echo "Now processing week $week og the month $month"
#do some commands
echo "Done"
fi
fi
done
done
A10. Biểu thức chính quy (Regular
Expressions)Một biểu thức chinh quy là một dạng thức
chung của những chuỗi các kí tự khác nhau. Dạng thức này được
biểu thị bởi một chuỗi kí tự đặc biệt thường được
dùng trong một cuộc
tìm kiếm. Mỗi dạng thức như vậy thường được đóng giữa hai kí tự
gọi là các kí tự giới hạn
(delimited character) thường là kí tự
/
. (Tuy nhiên, tuỳ theo nhu cầu, kí tự giới hạn có thể được định
nghiã lại).
Thí dụ: với biểu thức
/Bob/
thì dạng thức Bob
sẽ tương hợp với các chuỗi kí tự (string) có chứa chuỗi "Bob"
ở bất kì vì trí nào nó (như là các
chuỗi kí tự
BabyBob, my Boby, Bob, 12Bob, (Bob)
đều thoả mãn điều kiện tìm kiếm đối với dạng thức
Bob.)
Một biểu thức chính quy thường gọi tắt là RE (từ
chữ Regular Expressions).
Một chuỗi kí tự S thoả mãn dạng thức của một biểu thức chính quy R thì S
được gọi là tương hợp (với dạng thức của biểu thức R)
Một biểu thức chính quy R có thể là tổ hợp
của nhiều biểu thức chính quy thành phần SR. Khi đó, ta bảo SR là biểu
thức chính quy con (hay gọn hơn là biểu thức con) của R
Để mô tả dạng thức của các chuỗi kí tự cần tìm, trong mỗi biểu thức chính quy, sẽ cần
sử dụng một số kí tự mà các kí tự này được gán cho những ý nghiã
mới khác
với ý nghiã của một kí tự thông thường. Những kí tự được quy uớc để có riêng các ý
nghiã đặc biệt được gọi là các siêu kí tự. Nhiều mệnh lệnh trong
Linux sẽ chấp nhận và sử dụng các siêu kí tự này (và đôi khi với ý nghiã không
hoàn toàn giống nhau cho từng lệnh) trong đó có các lệnh
vi, grep, sed, awk,
và
perl
Các siêu kí tự thông thường
(Lưu ý: Các dấu
'
dùng trong bảng là để phân định biên
giới của chuỗi thí dụ - nhưng không thuộc nội dung của các chuỗi kí tự này)
Siêu Kí tự |
Ý nghiã |
Thí dụ (RE) |
Một số
string tương hợp
(Ngăn cách bởi dấu ,) |
Sẽ không tương hợp với
(Ngăn cách bởi dấu
,) |
^ |
Bắt đầu của dòng chữ |
/^Hello/ |
Tất cả các dòng bắt đầu với
'Hello' |
'A Hello',' Hello','_Hello' |
$ |
Kết
thúc dòng |
/the END$/ |
Tất
cả các dòng kết thúc bởi
'the END' |
' END','the END.','the
end' |
. |
Một
kí
tự bất kì ngoại trừ dấu xuống hàng |
/B..l/ |
'Bill', 'B12l', 'B#(l'; ... |
'Bil','Tool','Boom' |
* |
Dãy
kí tự bất kì tiếp theo sau kí tự ngay trước nó |
/ab*c/ |
'abc','abbxbc',
'ac',... |
'abb','abcc' |
[ ] |
Tương
hợp với một kí tự duy nhất nào đó bên trong ngoặc vuông |
/t[ou]/ |
'touch','tune','so
stupid' |
'do',
'tio', 'dual', 'two', 'tiu' |
[x-y] |
Tương
hợp với một kí tự duy nhất nào đó trong dãy thứ tự ASCII từ x đến y |
/^[A-Z]ove/ |
Tất cả các string bắt đầu với một chữ in hoa và nối theo là
'ove'
như là:
Love, Move,... |
'love', 'aove',
'myLove' |
[^ ] |
Tương hợp với một kí tự
sao cho nó không có mặt trong ngoặc vuông |
/file[^1-9]/ |
'file' , 'file0',
myfileA |
file1','xfile2',..., 'myfile9' |
\ |
Kí tự thoát cho các siêu
kí tự. Khi đó siêu kí tự đứng ngay sau dấu /sẽ không còn ý nghiã
đặc biệt mà chỉmang ý nghiã thông thường |
/the
end\./ |
'the end.','to
the end. (y/n)?' |
'the endy', 'the
end\.' |
Những siêu kí tự thêm vào
có thể được hỗ trợ bởi nhiều
chương trình
UNIX/Linux
|
\< |
Bắt đầu biên giới của
một từ (word) |
/\<Poison/ |
'OK Poison1' '
Poisoning' |
'EulerPoison',
'2Poison' |
\> |
Kết thúc biên giới của
một từ |
/Cap\>/ |
'No
Cap','HCap y', 'Cap.' |
'Capital' |
\( \) |
Các
thẻ quy ước \<n>: dùng để thay thế cho chuỗi các kí tự tương hợp trong
ngoặc đơn.
\1, đại diện cho RE
\( \)
thứ nhất,
\2
đại diện cho
RE \( \)
thứ nhì ....
Có tổng cộng 9 thẻ quy ước \1, \2, ...\9 |
/\(hard\)ship \1y/ |
RE
\( \)
đầu tiên từ bên trái kí hiệu bởi \1
RE
\( \) kế
tiếp kí hiệu là \2,..
Thí dụ này tường đương với biểu thức
/hardship hardy/ |
|
x\{m\} |
lập lại kí tự x m lần |
/ab\{3\}c/ |
tương đương với RE
/abbbc/ |
|
x\{m,n\} |
Lập lại kí tự x ít nhất m lần và không quá n lần |
/ab\{3,5\}cd/ |
'abbbcd', '
abbbbcde', 'abbbbbcdt' |
'abbcd',
'abbbbbbcd' |
Thí dụ:
/[a-z0-9]/
:
là RE tương hợp với tất cả các dòng chữ có chữ cái không viết hoa
hay các kí tự số
/[0-9]*\*$/ :
là RE tương hợp với tất cả các dòng chữ có một kí tự số theo sau
đó là một dãy kí tự bất kì và dòng này kết thúc bởi dấu
*
/^A\<f.th\>/ :
là RE tương hợp với tất cả các dòng chữ bắt đầu bằng từ "A"
sau đó từ thứ nhì bắt đầu bằng chữ f
tiếp theo là một kí tự nào đó và kết thúc của từ này bằng hai kí
tự
th.
Trong các phụ lục của
bài này và các bài sau đó sẽ có hướng dẫn dùng các lệnh quan trọng
của Linux có hỗ trợ chế độ dùng RE
A11 Lệnh
grep
Lệnh grep rất thông dụng để tìm và hiển thị lại
các dòng tương hợp của một dạng thức cho sẵn trong một hay nhiều
tập tin. Nếu dạng thức có chứa các kí tự không thấy được (như là
các kí tự khoảng trống, nhảy bước, hay xuống hàng) thì dạng thức
dùng trong mệnh lệnh phải được để trong các
dấu ngoặc dùng cho chuỗi kí tự.
grep in ra kết qủa lên stdout
và sẽ không thay đổi nội dung hay thuộc tính cài đặt từ trước của
các tập tin mà nó đọc. Ngoài các siêu kí tự thông thường,
grep hỗ trợ khá nhiều các
siêu
kí tự thông thường và hỗ trợ
thêm
một số khác nữa.
A11.1 lệnh
grep:
Cú pháp cho lệnh
grep
là:
grep
<Tham_Số>
<Dạng_thức>
<TÊN_CÁC_TẬP_TIN>
Lưu ý: như các lệnh khác, lệnh
grep
thường hay được dùng thông qua một ngỏ xuất từ một ống dẫn
truyền một cách rất tiện lợi (xem thí dụ dưới đây).
Thí dụ1:
#print the line of name.txt that contains substring 'Thomas' or
'thomas'
grep '[Tt]homas' /data/name.txt name2.txt
# using pipe output from ps -A command find all lines contains tty followed
by any character
ps -A | grep 'tty.'
A11.2 lệnh
grep
mở rộng
Ngoài ra, nếu dùng tham số
-E thì lệnh grep được gọi là grep mở rộng
(extended grep). Lệnh mở rộng này có định nghiã thêm một số siêu kí tự
mới. Và như vậy cú pháp viết thành:
grep -E
<Tham_Số_Khác>
<Dạng_thức>
<TÊN_CÁC_TẬP_TIN>
Các siêu kí tự thêm vào để dùng với lệnh
grep
-E
Siêu kí tự |
Ý nghiã |
Thí dụ (RE) |
Tương hợp với |
Không tương hợp với |
\w |
kí tự là một chữ cái hay một
chữ số |
f\w*\.dat |
file.dat, f1.dat,
... |
f_.dat,
f-i.dat |
\W |
kí tư không phải là chữ cái
hay chữ số |
file\W |
tương đương với RE:
file[^a-zA-Z0-9] |
f1,
file |
\b |
biên giới của một từ |
\bfile\b |
' file', ' file ' |
myfile, file1 |
+ |
Ít nhất một kí tự (hay nhiều
hơn) tương hợp
(trùng) với kí tự đứng liền ngay trước dấu + |
[a-z]+hood |
childhood, robinhood, thood |
ROBINhood, 1hood |
? |
Có tối đa một kí tự tương
hợp
(trùng)với kí tự liền trước nó |
bo?t |
boot, bot |
booot, bt |
| |
Tương thích hoặc |
love|like| want |
love, like, want |
|
( ) |
Các dạng thức nhóm trong
ngoặc |
want(ed|ing|er) |
wanted, wanting, wanter |
|
Thí dụ:
nội dung của file.dat:
#remark line
#this is data file of my flight
SouthWest SW Houston Dallas
8:00 9:00 40
NorthWest NW NewYork
OrangeCounty 6:00 12:00 65
continental CT Hongkong L.A.
7:00 8:00 125
-a
NEW CTT
Hongkong LA
6:00 19:00 212
Western CT2 Hanoi
Houston
Grep commands
cat file.dat | grep West
(Hai dòng đầu và dòng cuối sẽ tìm thấy và in ra)
grep -E '\bCT\b' file.dat
(Chỉ tìm thấy dòng thứ 3 và in ra)
POSIX: Bên cạnh các siêu kí tự kể trên,
grep -E
còn hỗ trợ các dạng POSIX ( portable operating system Interface
nghiã là "giao diện hệ điều hành khả xuất"). Đây là tiêu chuẩn
kĩ nghệ để bảo đảm các chương trình là khả xuất (portable) qua
các hệ điều hành
Kí tự
POSIX
hỗ trợ bởi grep
(Tất cả các thí dụ đưới đây dùng cùng một nguồn là
file.dat)
Lớp
ngoặc vuông |
Ý nghiã |
Thí dụ |
Trả về
từ
file.dat |
[:alnum:] |
các kí tự chữ và số |
grep
-E 'CT[[:alnum:]]' file.dat |
Hiển thị hai hàng cuối
|
[:alpha:] |
kí tự chữ |
grep
-E
'CT[[:alpha:]]' file.dat |
Dòng thứ
4 |
[:cntrl:] |
kí tự điều khiển
(như F1, F2,...) |
|
|
[:digit:] |
kí tự số |
grep
-E
'CT[[:digit:]]' file.dat |
Dòng
cuối cùng |
[:graph:] |
kí tự không phải là
các khoảng trống |
grep
-E '^[[:graph:]]'
file.dat |
Hai
dòng đâu |
[:lower:] |
kí tự dạng chữ
thường |
|
|
[:print:] |
giống như
[:graph:] nhưng cộng
thêm các kí tự khoảng trống |
|
|
[:punct:] |
kí tự chấm câu |
grep
-E 'L[[:punct:]]'
file.dat |
Dòng
thứ 3 |
[:space:] |
các kí tự khoảng
trống
(đầu dòng, không khí, tab) |
grep
-E '^[[:space:]][[:space]]' file.dat |
Tất
cả các dòng ngoại trừ hai dòng đâu tiên. |
[:upper:] |
Viết Hoa |
|
|
[:xdigit:] |
số trong
hexadecimal |
|
|
A11.2 Các tham số thông
dụng:
Sau đây là một số tham số rất thông dụng của lệnh grep:
-
-A
<Số_Dòng> : Hiển thị dòng tìm thấy tương hợp và hiển thị
tiếp số
<Số_Dòng> các dòng tiếp sau dòng tương hợp này
-
-a
: xử lí tập tin nhị phân xem như nó là tập tin văn bản. Thí dụ: Hãy
thử so sánh hiển thị trả về của hai lệnh
grep 'do not sort' /bin/ls
và
grep -a 'do not sort' /bin/ls
-
-B
<n>:
in ra luôn số dòng của các dòng chữ tương hợp ở đầu mỗi dòng hiển thị (chẳng
hạn:
grep -B 2 'Hanoi' file.dat
)
-
-b
: in thứ tự byte (byte offset)
của các dòng tương hợp xuất hiện trong tập tin (chẳng
hạn:
grep -b 'Hanoi' file.dat
)
-
-c
: Chỉ in ra số các dòng tương
hợp (mà không in bản thân dòng có chuỗi kí tự tương hợp này)
(thí dụ: grep -c Houston file.dat
)
-
-i
:
Không phân biệt chữ in hoa hay in thưòng
(thí dụ:
grep -i hongkong file.dat
)
-
-l:
In tên tập tin có dòng tương hợp thay vì dùng định dạng thông thường.(
grep -l Hongkong file.dat
)
-
-m
<n>:
ngưng đọc từ ngỏ vào nếu như đã đạt tới n dòng tương thích (
grep -m 1 Hongkong file.dat)
-
-n: In
số thự tự của dòng trước khi in mỗi dòng tương hợp (
grep -n Hongkong
file.dat)
-
-r
: đọc thi
hành lên tất cả các tập tin có trong thư mục (thi hành
grep đệ quy)
A12 Lệnh
xargs:
Đây là một lệnh rất hữu hiệu để chuyển các ngỏ ra hay các thông
báo từ các ngỏ ra chuẩn thành các tham số của những mệnh lệnh khác nhằm thực thi
cùng thao tác (một lệnh hay 1 nhóm lệnh) trên nhiều đối tượng khác nhau (các đối
tượng này đóng vai trò tham số)
xargs
sẽ đọc từng dòng từ ngỏ vào chuẩn của nó để chuyển
thành một tham số để thi hành cùng một lệnh cho mỗi dòng đó lần lượt.
Chẳng hạn, nếu ngỏ ra cuả một mệnh lệnh nào đó bao gồm 3
dòng X1, X2, X3 và ngỏ ra này được ống dẫn truyền (pipe) cung cấp cho lệnh
xargs để thực thi một lệnh L thì lệnh
xargs sẽ thự thi tổng cộng 3 dòng lệnh L1,
L2, L3. Trong đó, dòng lệnh L1 sẽ lấy X1 làm tham số. L2 sẽ lấy X2 làm tham số
và L3 lấy X3 làm tham số. Các thí dụ sẽ minh hoạ rõ hơn điều này.
Cú pháp thông dụng nhất là:
<Lệnh_1>
| xargs <Tham_Số>
<Lệnh_2>
Nếu <Lệnh_2>
không có mặt thì xargs -i chỉ hiển thị ra màn hình theo sự
cài đặt của <Tham_Số>.
<Lệnh_1>
thường là các lệnh xuất ra nhiều dòng tới stdout, mỗi dòng (hay một vài
dòng) như vậy sẽ được gom lại vào thành một bộ tham số cho các dòng
<Lệnh_2>
theo thứ tự một cách lần lượt cho đến khi không còn dòng xuất nào nữa từ
<Lệnh_1>
A12.1 Ứng dụng
Thí du1:
Thí dụ sau sẽ chuyển tất cả các tập tin bắt đầu bằng
my
và có phần mở rộng là
.txt
trong thư mục
test
(kể cả các tập tin trong các thư mục con của
test)
vào trong một thư mục mới tên là
mydir
find ./test -name "my*.txt" | xargs -i mv {} ./mydir
Ở đây, kí hiệu
{}
thay thế cho các dòng xuất ra từ lệnh
find ./test -name "my*.txt".
Như vậy, cứ mỗi tập tin dạng
my*.txt tìm được qua lệnh
find thì tên (đầy đủ) của tập tin này sẽ được
thay vào trong ngoặc
{}
để lần lượt được lệnh
mv <tên_tập_tin_cung cấp_từ_lệnh_find> ./mydir
di chuyển nó vào trong thư mục
./mydir.
Lưu ý: trong một số trường hợp (như là khi dùng với các lệnh hỗ trợ
biểu thức chính quy chẳng hạn) thì người dùng thay vì dùng kí hiệu
{}, có thể
định nghiã lại một cặp kí hiệu khác chẳng hạn như căp kí hiệu
~~, $$, [], ##,
hay bất kì kí hiệu nào ... Tuy nhiên, một số kí hiệu cần phải dùng chung với kí
tự thoát \ để tránh gây lỗi mập mờ
(ambiguity).
Như vậy lệnh trên hoàn toàn tương đương
với lệnh
find ./test -name "my*.txt" | xargs -i[] mv [] ./mydir
Ở đây,
{}
đã được thay bằng cặp kí tự
[]
Trong thí dụ trên, người dùng có thể
thay lệnh
mv
và lệnh
find
bởi các lệnh thích hợp để làm các thao tác khác
Thí du2: Lệnh
grep sau đây tìm tất cả các tập tin kết thúc
bởi
txt được liệt kê trong
filelist và chuyển cho lệnh
xargs
lấy làm tham số cho lệnh
rm
để xoá các tập tin đó.
#content of "filelist"
./test/mytest.txt
./myfiletxt
./introduction.pdf
Khi thực thi lệnh
grep 'txt$' filelist |xargs -i rm -f {}
thì 2 dòng
./test/mytest.txt
./myfiletxt
sẽ được grep lọc lựa và hiển thị thông qua
ống dẫn truyền sẽ đưuợc lệnh
xargs tiến hành thành 2 dòng lệnh:
rm -f ./test/mytest.txt
rm -f ./myfiletxt
Hậu quả là 2 tập tin trên sẽ bị xoá (nếu có)
Thí dụ3:
Lệnh sau đây sẽ hiển thị thời gian và chào người ra lệnh trong
cùng một hàng.
(date; echo "Hello, `whoami`.") |xargs
Thí dụ4:
Lệnh sau sẽ hiển thị lại tên, họ, ngày sinh, nghề nghiệp của một
tâp tin
#content of personal.txt
#first line is name
#secondline is DOB
#last line is Ocupation
Hung Nguyen
12/12/90
Engineer
PhuongDung Vo
04/28/63
Pharmacist
Long Le
08/03/79
Dentist
#End of file personal.txt
Lệnh đơn giản sau đây sẽ hiển thị các thông tin về một người (gồm 3 dòng) trong
một dòng. Dòng bỏ trắng sẽ bị tự động loại ra
(bởi lệnh
xargs), các đòng bị chú sẽ bị loại bỏ bởi
lệnh grep -v
grep -v "^#" personal.txt | xargs -n3
Một ứng dụng khác của tham số -n
là dùng để cung cấp nhiều tham số cho cùng một lệnh.
Thí dụ5: Thực thi lệnh
chép các tập tin có tên
mysourcefile<i>
thành mytargetfile<i>
mà danh sách đã được chuẩn bị sẵn trong tập tin
myhandle
như sau:
#content of myhandle
mysourcefile1
mytargetfile1
mysourcefile2
mytargetfile2
mysourcefile3
mytargetfile3
#End of myhandle
grep -v '^#' myhandle |xargs -i -n2 cp -f
Trong thí dụ trên, lệnh grep
sẽ hiển thị nội dung của tập tin myhandle
ngoại trừ các dòng bắt đầu bằng dấu #
(tức là loại trừ các dòng bị chú). Sau đó, lệnh
xargs
sẽ đưa một lúc 2 dòng (tương ứng với -n2) vào cho lệnh
cp.
Dòng đầu sẽ là tên tập tin nguồn (mysourcefile<i> ) cho
tham số thứ nhất của lệnh
cp và dòng thứ nhì (mytargetfile<i> )
trở thành tham số thứ nhì cho lệnh
copy.
Nghiã là Nếu các
mysourcefile<i>
tồn tại thì chúng sẽ lần lượt được chép
(copy) thành mytargetfile<i>
một cách tương ứng.
A12.2 Các tham số thông dụng cho lệnh
xargs:
-
-a
<Tên_Tập_Tin>
: Đọc ngỏ vào từ
<Tên_Tập_Tin>
thay vì từ ngỏ vào chuẩn. Nếu dùng tham số này, stdin
sẽ không thay đổi khi các lệnh đang chạy. Ngoài ra, stdin sẽ được chuyển hướng
tới
/dev/null.
-
-0 Các dòng thông tin đọc từ ngỏ vào được kết thúc bởi
kí tự null thay vì bởi các kí tự khoảng trắng (whitespace); ngoài ra, các dấu
ngoặc (" , '
) cũng như dấu nghiên về (backslash)
\
không còn được mang ý nghiã đặc biệt nữa.
Hữu dụng khi ngỏ vào có chứa các kí tự trắng, các dấu ngoặc, hay dấu nghiêng về.
Lệnh dùng tham số dạng
find -print0 của GNU sẽ được tiện lợi với
cách gọi này.
-
-i<Chuỗi_Thay_Thế>
Thay vì dùng cặp kí hiệu mặc định là {},
có thể dùng bất kì kí hiệu thay thế nào. Cặp kí hiệu đó sẽ trong khi thực
thi sẽ được thay thế bằng mỗi dòng xuất ra từ stdout và thi hành lần lượt
cho đến khi không còn dòng nào từ stdout nữa.
Lưu ý: Một khi dùng
<Chuỗi_Thay_Thế>
(hay dùng
{}
trong mặc định) để cung cấp tham số cho mệnh lệnh
cần thi hành (<Lệnh_2>)
thì tham số này sẽ lấy quyền ưu tiên đối với tham số
-n<Số_Đối_Số_Tối_Đa>
và do đó, có thể tạo ra các hiệu ứng không mong muốn. Do đó, nên tránh
viết <Chuỗi_Thay_Thế>
(hay
{} )
vào trong dòng lệnh như là tham số nếu người dùng dự tính khai thác tham số
-n
-
-n<Số_Đối_Số_Tối_Đa>
Sử dụng
<Số_Đối_Số_Tối_Đa>
làm các đối số cho mệnh lệnh mà
xargs gọi.
-
-p
Hiển thị câu hỏi sự chuẩn y của
người dùng trước khi lệnh được thi hành. Lệnh được thi hành chỉ khi người
dùng trả lời `y '
hay `Y '
-
-r
Nếu stdin của xargs chỉ là các
dòng trắng thì không thực thi mệnh lệnh
A13 Ôn luyện
và đào sâu về sử dụng đổi hướng và Ống truyền tên
A13.1 Đổi hướng
Đổi hướng: là quá trình bắt các
thông tin ngỏ ra từ một mệnh lệnh, một tập tin, một chương trình hay ngay cả một
đoạn mã và gửi chúng vào ngỏ vào của một mệnh lệnh, một tập tin hay một chương
trình khác. trong bài 1 chúng ta đã nói về việc đổi hướng. Giờ là lúc đào
sâu thêm các chi tiết.
Bộ mô tả tập tin (file descriptor):
Tương tự C/C++ hay các ngôn ngữ khác, mỗi tập tin được mở có thể được gán lên đó
một bộ mô tả tập tin (gọi tắt là bộ mô tả). Trong các văn lệnh, mỗi bộ mô
tả có thể được gán cho một con số. Riêng các con số 0, 1, 2 được mặc định lần
lược dùng cho các bộ mô tả stdin, stdout, và stderr. Các con số khác từ 3
tới 9 có thể được dùng để gán cho bất kì tập tin được mở nào khác.
Việc đổi hướng có thể được hoàn tất bằng cách
dùng các kí tự
đổi hướng, bằng cách kết hợp với lệnh
exec, hay bằng cách kết hợp với
các vòng lặp và rẽ nhánh
A13.1.1 Dùng kí tự đổi hướng
> <TÊN_TẬP_TIN>
: Đổi hướng vào
<TÊN_TẬP_TIN>
xoá nội dung có sẵn từ trước của <TÊN_TẬP_TIN>
Thí dụ1: ls -l >
list_file.txt
cat list_file.txt
Thí dụ2: mount /dev/df0 /mnt/flopy
1>/dev/null 2>/dev/null
# redirect output and error messages into NULL (hide messages released
from 'mount')
: >
<TÊN_TẬP_TIN>
: Cắt bỏ
<TÊN_TẬP_TIN>
thành tập tin có độ dài 0 byte
>>
<TÊN_TẬP_TIN> :
Chép nối stdout vào nội dung của
<TÊN_TẬP_TIN>
(tạo ra tập tin mới nếu nó chưa tồn tại)
Thí dụ3:
LOG=./logfile.txt
echo "date" > $LOG
echo "the content of /home directory :" >> $LOG
ls -l >> $LOG
# to read what is in log file, invoke command cat ./logfile.txt
2>&1 đổi hướng stderr sang stdout (
tức là chuyển các thông bao lỗi vào cùng một ngỏ ra chuẩn)
Đóng ngỏ ra của bộ mô tả n
Đóng stdout
Thí dụ4:
read all content of hard drive /dev/hdb and save into a file
named as rawbackup.dat
dd < /dev/sdb > ./rawbackup.dat
note: you may also compress the
output file rawbackup.dat to save space! However, the file must be decompressed
in each restore process
Thí dụ5: restore the
rawbackup.dat into the hard drive /dev/hdc
dd <./rawbackup.dat >/dev/hdc
A13.1.2 Dùng kết hợp với
lệnh exec:
exec <
<TÊN_TẬP_TIN>
được dùng để đổi hướng đọc từ stdin sang đọc từ một tập tin. Cho nên sau khi sử
dụng lệnh này, mọi dữ liệu đều được đọc từ tập tin
<TÊN_TẬP_TIN>
thay vì từ stdin.
Ứng dụng của nó là có thể dùng văn lệnh để đọc hay
điều chỉnh nội dung của một tập tin
Thí dụ6:
#!/bin/bash
# Redirect stdin by 'exec'.
exec 4<&0
# save stdin to descriptor #4.
exec < data.txt # stdin is now
replaced by file "data.txt"
read line1
# Read the first line of file "data.txt".
read
line2
# Read second line of file "data.txt"
read
line3
# and the third line
echo
echo "3 first lines read from data.txt are:"
echo "1st line: $line1
echo "2rd line: $line2
echo "3th line: $line3"
echo; echo; echo
exec 0<&4 4<&- # restore the original stdin back
exit 0
Thí dụ7:
echo 1234567890 > MyFile # Write string to "File".
exec 5<> MyFile
# Open "File" and assign fd 5 to it.
read -n 2 <&5 # Read only
1st 2 characters; current cursor move to offset 2
echo -n 'hi' >&5 # Write a 'hi' there.
exec 5>&- # Close fd 5.
cat MyFile # Should be 12hi567890
A13.1.3 Dùng kết hợp với các vòng lặp hay mã rẽ nhánh
Các vòng lặp hay mã rẽ nhánh có thể được dùng kết hợp qua kí tự
đổi hướng
<
Thí dụ8: Giả sử tập tin file.dat kết thúc
bằng dòng chữ "THE END"
while [ ! "$line" = "THE END." ]
do
read line
# Reads from file.dat, rather than stdin.
echo $line
let "count += 1"
if [ $count -gt 3 ]; then
echo "out of range"
break
fi
done <"file.dat" #
Redirects stdin to file file.dat.
echo "$count"
#display content of file.dat
A13.2 Ống Dẫn Truyền Tên
(named pipe):
Trong UNIX/Linux, một kênh trao đổi giữa các
tiến trình, FIFO (từ chữ "first-in, first-out"), thường được dùng để chuyển
các ngỏ ra chuẩn của một mệnh lệnh lên ngỏ vào chuẩn của một mệnh lệnh khác đang
chạy cùng lúc. Đôi khi các tiến trình (process) có thể tự mở các ống dẫn truyền
đến các lệnh mà chúng khởi động
Quá trình đổi hướng có thể được xem là một ống dẫn truyền dạng đơn giản. Chúng
ta sẽ khai thác kĩ hơn 2 dạng named pipe đơn giản.
A13.2.1
command1 | command2 | command3 ... :
Dùng để xử lí một dãy các lệnh mà ngỏ ra của lệnh trước chuyển thành ngỏ vào của
của sau và chúng ngăn cách nhau bởi dấu
|
Thí dụ1:
Xếp thứ tự tất cả các dữ liệu xuất từ các tập tin
*.dat, xoá các dòng trùng nhau, và lưu trữ lại trong tệp
newdata
cat *.dat | sort | uniq > newdata
Thí dụ2:
tìm tất cả tệp *.txt
trong thư mục hiện hoạt rồi xử lí qua lệnh
ls -l
và sau cùng lọc bằng lệnh
grep các
tập tin được tạo ra trong tháng 12
find ./ -name *.txt | xargs -i ls -l | grep "Dec"
#this command may be used to find all *.txt file that created in December
A13.2.2
<(command) :
Dùng để gửi ngỏ ra của một lệnh vào một tiến trình khác
Thí dụ3
Lệnh
diff so sánh thuộc tính của
mọi tập tin trong thư mục dir1
với thư mục dir2
mà các thuộc tính đó lại được rút ra từ hai lệnh
ls-l
diff <(ls -l dir1) <(ls -l dir2)
Thí dụ4:
Một số lệnh tạo ra tập tin nén dạng *.tar.gz:
tar cpf >(gzip -9 >
myfile.tar.gz) myDir
#The command will create a tar of
myDir and then compress this into a file named as myfile.tar.gz
# It is equivalent to
gzip -9 < pipe > myfile.tar.gz
&
tar cpf pipe myDir
rm pipe
Thí dụ5:
đọc thông tin từ lệnh
lsmod
(hiển thị gồm 3 cột) lần lượt vào các biến
record<i>
và hiển thị chúng
while read record1 record2 record3; do
echo $record1 $record2 $record3
done < <(lsmod)
# Output may look like:
# Modules Size Use by
Not tained
# smbfs 40352 2
(Autoclean)
# nls_iso8859-1 3520 0 (Autoclean)
#let try to compare with the command lsmod itself
#The similar pipe command is
lsmod | while read record1 record2 record3; do
echo $record1 $record2 $record3
done # Same output as above.
Lưu ý:
các
phương pháp dùng pipe trên tương dương với việc sử dụng FIFO trong C. (thông
qua các hàm: popen, pclose ...)
Bài kì tới: Biến môi trường, Các ki hiệu quy ước
riêng của BASH, Hàm, sed, và daemon
© http://vietsciences.free.fr , http://vietsciences.org
và http://vietsciences2.free.fr
Làng Đậu