Không thể tưởng tượng ngôn ngữ lập trình nào thiếu khái niệm mảng. Không quan trọng chúng được triển khai như thế nào trong các ngôn ngữ khác nhau. Mảng giúp gom nhóm dữ liệu, giống nhau hoặc khác nhau, dưới một tên tượng trưng.
Vì chúng ta quan tâm đến shell scripting, bài viết này sẽ giúp bạn thao tác với các shell script sử dụng khái niệm mảng.
Khởi tạo và sử dụng mảng
Với các phiên bản bash mới hơn, hỗ trợ mảng một chiều. Mảng có thể khai báo rõ ràng bằng builtin declare của shell.
declare -a var
Nhưng không bắt buộc phải khai báo biến mảng như trên. Có thể chèn phần tử riêng lẻ trực tiếp như sau:
var[XX]=<giá_trị>
trong đó 'XX' chỉ chỉ số mảng. Để tham chiếu phần tử mảng, dùng cú pháp ngoặc nhọn:
${var[XX]}
Lưu ý: Chỉ số mảng luôn bắt đầu từ 0.
Cách tiện lợi khác để khởi tạo toàn bộ mảng là dùng cặp ngoặc đơn như sau:
var=( phần_tử1 phần_tử2 phần_tử3 ... phần_tửN )
Còn một cách gán giá trị cho mảng khác. Cách này là trường hợp con của phương pháp vừa giải thích.
array=( [XX]=<giá_trị> [XX]=<giá_trị> ... )
Chúng ta cũng có thể đọc/gán giá trị cho mảng lúc chạy bằng builtin read của shell.
read -a array
Khi thực thi câu lệnh trên trong script, nó chờ nhập liệu. Cần cung cấp các phần tử mảng cách nhau bằng dấu cách (không phải xuống dòng). Nhấn Enter để kết thúc.
Để duyệt qua các phần tử mảng, cũng có thể dùng vòng lặp for:
for i in "${array[@]}"
do
# truy cập mỗi phần tử như $i
...
done
Script sau tóm tắt nội dung phần này:
#!/bin/bash
array1[0]=one
array1[1]=1
echo ${array1[0]}
echo ${array1[1]}
array2=( one two three )
echo ${array2[0]}
echo ${array2[2]}
array3=( [9]=nine [11]=11 )
echo ${array3[9]}
echo ${array3[11]}
read -a array4
for i in "${array4[@]}"
do
echo $i
done
exit 0
Các phép toán trên mảng
Nhiều phép toán chuỗi tiêu chuẩn hoạt động trên mảng. Xem script mẫu sau thực hiện một số phép toán trên mảng (kể cả phép toán chuỗi).
#!/bin/bash
array=( apple bat cat dog elephant frog )
# in phần tử đầu tiên
echo ${array[0]}
echo ${array:0}
# hiển thị tất cả phần tử
echo ${array[@]}
echo ${array[@]:0}
# hiển thị tất cả trừ phần tử đầu
echo ${array[@]:1}
# hiển thị phần tử trong khoảng
echo ${array[@]:1:4}
# độ dài phần tử đầu
echo ${#array[0]}
echo ${#array}
# số lượng phần tử
echo ${#array[*]}
echo ${#array[@]}
# thay thế chuỗi con
echo ${array[@]//a/A}
exit 0
Kết quả khi chạy script trên:
apple apple apple bat cat dog elephant frog apple bat cat dog elephant frog bat cat dog elephant frog bat cat dog elephant 5 5 6 6 Apple bAt cAt dog elephAnt frog
Tôi nghĩ không cần giải thích chi tiết script trên vì nó tự giải thích. Nếu cần, tôi sẽ dành một phần riêng trong series này cho thao tác chuỗi.
Thay thế lệnh với mảng
Thay thế lệnh gán đầu ra của một hoặc nhiều lệnh vào ngữ cảnh khác. Trong ngữ cảnh mảng, chúng ta có thể chèn đầu ra của lệnh làm các phần tử riêng lẻ của mảng. Cú pháp như sau:
array=( $(command) )
Mặc định, nội dung trong đầu ra của lệnh cách nhau bằng khoảng trắng sẽ được nhét vào mảng như các phần tử riêng lẻ. Script sau liệt kê nội dung thư mục, là các file có quyền 755.
#!/bin/bash
ERR=27
EXT=0
if [ $# -ne 1 ]; then
echo "Usage: $0 <đường_dẫn>"
exit $ERR
fi
if [ ! -d $1 ]; then
echo "Thư mục $1 không tồn tại"
exit $ERR
fi
temp=( $(find $1 -maxdepth 1 -type f) )
for i in "${temp[@]}"
do
perm=$(ls -l $i)
if [ `expr ${perm:0:10} : "-rwxr-xr-x"` -eq 10 ]; then
echo ${i##*/}
fi
done
exit $EXT
Mô phỏng mảng hai chiều
Chúng ta dễ dàng biểu diễn ma trận 2 chiều bằng mảng 1 chiều. Trong biểu diễn theo hàng (row major order), các phần tử trong mỗi hàng của ma trận được lưu dần vào chỉ số mảng theo thứ tự tuần tự. Với ma trận mXn, công thức như sau:
matrix[i][j]=array[n*i+j]
Xem script mẫu khác để cộng 2 ma trận và in ma trận kết quả.
#!/bin/bash
read -p "Nhập cấp ma trận [mxn] : " t
m=${t:0:1}
n=${t:2:1}
echo "Nhập phần tử cho ma trận đầu tiên"
for i in `seq 0 $(($m-1))`
do
for j in `seq 0 $(($n-1))`
do
read x[$(($n*$i+$j))]
done
done
echo "Nhập phần tử cho ma trận thứ hai"
for i in `seq 0 $(($m-1))`
do
for j in `seq 0 $(($n-1))`
do
read y[$(($n*$i+$j))]
z[$(($n*$i+$j))]=$((${x[$(($n*$i+$j))]}+${y[$(($n*$i+$j))]}))
done
done
echo "Ma trận sau khi cộng là"
for i in `seq 0 $(($m-1))`
do
for j in `seq 0 $(($n-1))`
do
echo -ne "${z[$(($n*$i+$j))]}\t"
done
echo -e "\n"
done
exit 0
Dù có giới hạn khi triển khai mảng trong shell scripting, nó hữu ích trong một số tình huống, đặc biệt khi xử lý thay thế lệnh. Nhìn từ góc độ quản trị, khái niệm mảng đã mở đường cho việc phát triển nhiều script nền trong hệ thống GNU/Linux.