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ến
BASH không hỗ trợ cho các tính toán điểm chấm động (floating point). Tuy
nhiên, lệnh
bc có thể giúp tiến hành các loại toán này. Cú phép đơn giản
để tiến hành là:
<TÊN_BIẾN>=$(echo
"<Tham_Số>";
<Biểu_Thức_Số_Học>"
| bc [<Tham_Số_Ngắn>])
Giá trị của <Tham_Số>
Có thể là:
-
length số chữ số có nghiã trong biểu
thức
-
scale
số chữ số thập phân sau dấu chấm trong biểu thức
-
sqrt
(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ánh
12.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 định
Thí dụ4:
for FILE in [abc]*; do
rm -f $FILE
done
#this script will remove all files whose name beginning by
character a, b, or c in current directory
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>
done
for ((
<Biểu_Thức1>;
<Biểu_Thức2>;
<Biểu_Thức3>
)) ; do <KHỐI_LỆNH>
; done Trong đó,
<Biểu_Thức1>
sẽ được đánh giá sau đó là việc đánh giá của biểu
<Biểu_Thức2>
và mỗi lần
<Biểu_Thức2>
có giá trị khác 0, thì khối lệnh sẽ được thi hành và sau đó sẽ
là việc đánh giá
<Biểu_Thức3>.
Nếu bất kì biểu thức nào không có mặt thì chỗ thiếu vắng đó sẽ được
xem như là một biểu thức luôn luôn có giá trị 1. Giá trị trả về của
vòng lặp này là trạng thái thoát (exit status) của mệnh lệnh sau cùng
trong khối lệnh, hay trả về giá trị "false"
nếu các biểu thức là không hợp lệ.
Như là một bài tập, hãy thử nghiệm các vòng lặp for với biến chỉ số giảm dần
thay vì tăng như trong cú pháp nêu trên; đồng thời hãy thử dùng giá trị cực
tiểu (MIN) ỏ trên là số âm để xem khả năng hỗ trợ của BASH.
Lưu ý: Việc trình bày bất
kì một khối lệnh hay câu lệnh nào trong BASH đều có thể thay các kí tự xuống
hàng bởi các dấu
;
Thí dụ1:
for fruit in "red apple" "yellow orange" "green grape"
do
echo "color fruit : $fruit"
done
Thí dụ2:
for CHAR in `cat $myFile.txt`; do
echo "$CHAR"
done
Xem thêm thí dụ về vòng lặp
for trong phần nói về
danh sách
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)
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).
\< |
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' |
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ó |
boot |
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ụ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
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,...) |
grep -E 'CT[[:digit:]]'
file.dat |
Dòng cuối cùng |
[:digit:] |
kí tự số |
|
|
[: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
và http://vietsciences.
org
N