+1

Giới thiệu bash shell và cách dùng để cập nhật DB

bash.png

Mã bash shell là một danh sách những lệnh mà chúng sẽ được thực thi một cách tuần tự. Thông thường nội dung một file bash shell sẽ có đuôi .sh và bắt đầu bằng

#!/bin/bash

Đoạn mã đó chỉ ra rằng các mã lệnh nên được chạy trong bash shell, bất kể là người dùng đã chọn loại shell nào. Bởi vì có rất nhiều loại shell nên cú pháp của chúng cũng sẽ rất đa dạng và có thể là khác nhau ít hoặc nhiều.

Một ví dụ là dấu #, trong bash shell nó sẽ được hiểu là tất cả những gì theo sau # sẽ là comment và không được thực thi. Nhưng trong shell khác thì nó có thể không có nghĩa như vậy.

Biến

Cách khai báo

Bất kì một ngôn ngữ nào cũng phải có biến để sử dụng và bash shell cũng không ngoại lệ. Cách định nghĩa biến theo format : tên_biến=""

var="xin chao"

Và gọi đến biến bằng cách thêm dấu $ trước tên biến.

$var

Cơ bản là bash shell xem những cái phân cách nhau bởi dấu khoảng trắng là những lệnh và thực thi chúng nên bạn cần chú ý :

Chú ý 1: với toán tử gán = nói riêng và các toán tử khác nói chung, bạn không được để khoảng trắng trước và sau nó.

Chú ý 2: Bạn không cần thiết lúc nào cũng cần để "" bao bọc gía trị của biến, bạn có thể bỏ chúng nếu gía trị đó không chứa khoảng trắng.

Nháy đơn và nháy đôi với biến

Bạn hãy xem đoạn code sau và đoán xem bash sẽ in ra màn hình những gì :

#!/bin/bash
var="xin chao"
echo '$var'
echo "$var"

Kết qủa là đây :

$var
xin chao

Bạn đã thấy nếu cần expand biến (tức là tham chiếu đến gía trị của biến) thì dùng nháy đôi, còn nháy đơn sẽ không làm điều đó mà giữ nguyên biến. Nhưng nếu bạn muốn vừa giữ tên biến và cũng muốn lấy gía trị của nó trong dấu nháy đôi thì sẽ dùng \ :

echo "\$var=$var"

Kết qủa

$var=xin chao

Chú ý : Hãy sử dụng nháy đôi để bảo vệ biến của bạn, như "$var".Bởi vì nếu gía trị của biến là chuỗi rỗng hoặc có chứa khoảng trắng thì có thể sẽ có lỗi trong mã của bạn.

Ngoặc nhọn để bảo vệ biến

Bạn hãy thử chạy bash với dòng code như sau xem như nào

#!/bin/bash
X=brace
echo "$Xabc"

Bạn không thấy gì cả ? đương nhiên ! vì bash shell nhầm tưởng ý của bạn là biến Xabc và biến này thì bạn chưa định nghĩa nó. Nếu bạn muốn dùng như trên hãy dùng cặp ngoặc nhọn để bao quanh tên biến, khi đó shell sẽ hiểu bạn :

#!/bin/bash
X=brace
echo "${X}abc"

Lệnh rẽ nhánh if

Giới thiệu

Cũng như các ngôn ngữ lập trình, với một danh sách dài các lệnh thì không thể thiếu được câu lệnh rẽ nhánh cơ bản để kiểm tra xem điều kiện đúng hay sai mà thực thi những việc mình cần.

Câu lệnh đơn giản nhất sẽ có cấu trúc như dưới :

if điều kiện
then
	1 hoặc nhiều câu lệnh ở đây
fi

Và đương nhiên chúng ta sẽ có else như bao ngôn ngữ khác

if điều kiện
then
	1 hoặc nhiều câu lệnh ở đây
else
    xử lý khác ở đây
fi

Nhưng với elseif thì shell viết hơi khác 1 chút là elif, nhưng bạn cũng có thể dufngbao nhiêu elif tùy thích.

if điều kiện 1
then
	xử lý 1
elif điều kiện 2
then
	xử lý 2
fi

Các toán tử

Toán tử - nói đến rẽ nhánh thì đương nhiên những toán tử là bắt buộc phải có. Trong bash shell có nhưng toán tử cơ bản sau :

Toán tử Ý nghĩa Số lượng toán hạng
-n chuỗi không empty, độ dài lớn hơn 0 1
-z chuỗi empty, độ dài bằng 0 1
-d tồn tại thư mục có tên là ... 1
-f tồn tại file có tên là ... 1
-eq 2 toán hạng là số integer và nó bằng nhau 2
-neq ngược lại của-eq 2
= 2 toán hạng bằng nhau (chuỗi) 2
-lt toán hạng đầu nhỏ hơn toán hạng thứ 2 (cả 2 là số integer) 2
-gt toán hạng đầu lớn hơn toán hạng thứ 2 (cả 2 là số integer) 2
-le toán hạng đầu nhỏ hơn hoặc bằng toán hạng thứ 2 (cả 2 là số integer) 2
-ge toán hạng đầu lơn hơn hoặc bằng toán hạng thứ 2 (cả 2 là số integer) 2

Bạn có thể xem ví dụ sau để hiểu cách dùng if cũng như các tán hạng.

#!/bin/bash
A=1
B=2
string1="Not empty"
if [ $A -lt $B ]	# $A có nhỏ hơn $B không ?
then
	echo "${A} nhỏ hơn ${B}"
fi

if [ -z "$string1" ]; then # kiểm tra $string1 có empty không?
	echo "\${string1} rỗng"
else
    echo "\${string1} không rỗng"
fi

Vòng lặp

for

Sau đây là cú pháp của vòng lặp for :

#!/bin/bash
for A in 1 2 3
do
	echo $A
done

Như bạn thấy thì vòng lặp for sẽ duyệt qua tất cả các phần tử được phân cách nhau bởi dấu khoảng trắng, nên như chú ý ở trên với phần biến nếu bạn có khoảng trắng trong giá trị của biến thì hãy sử dụng cặp nháy ""

Trong bash shell thì kí tự * được hiểu là tất cả kí tự như trong phần về find tôi đã tôi nói, ví dụ *.bak thì bash sẽ hiểu là tất cả các file backup có đuôi .bak chẳng hạn. Nên ta sẽ có một ví dụ chắc sẽ hữu ích hơn như là tìm trog 1 thư mục chỉ định mà gặp file backup thì di chuyển đến thư mục /home/backup chẳng hạn.

#!/bin/bash
for A in *.bak
do
	mv $A /home/backup
done

while

Như bao ngôn ngữ khác thì While sẽ lặp đến khi điều kiện chỉ định còn là true.

#!/bin/bash
# lặp từ 1 đến khi bằng 100 thì thôi, in ra từ 1 ~ 99
A=1
while [ $X -le 99 ]
do
	echo $X
	X=$((X+1))
done

Mảng

Mảng trong shell cũng là một kiểu dữ liệu mà trong nó chứa danh các giá trị theo index như các ngôn ngữ lập trình. Cơ bản bạn sẽ khởi tạo được một biến mảng bằng cách :

#!/bin/bash
var[1]="test"
# truy cập mảng
echo ${var[1]}
# hoặc khai báo như dưới
var2=( [0]="mot" [1]="hai" [3]="bon" ) # vì phần tử thứ 3 của mảng không được khởi tạo nên nó sẽ là null
var3=( mot hai ba ) # phân cách các phần tử bằng dấu khoảng trắng

# để đếm số phần tử trong mảng bạn sẽ dùng kí tự # như sau
echo ${#var2[@]}
# hoặc
echo ${#var2[*]}

Ngoài ra còn một số thao tác khác với mảng bạn có thể xem thêm ở đây.

Dùng shell để cập nhật DB

Đúng vậy ! Shell hoàn toàn có thể làm được việc này, cũng giống như bạn xây dựng giao diện để người dùng thao tác vậy. Để kết hợp phần về find trong bài trước với kiến thức ở bài này về bash,tôi có viết một đoạn shell sau. Tiền để của nó là khách hàng có yêu cầu một chức năng bulk upload hàng loạt ảnh đã được đặt sẵn trong folder định trước với quy ước tên file là ID của nhân viên, mà làm việc này với tất cả hơn 120 người thì sẽ rất tốn công :

#!/bin/bash
# Dùng find để get tất cả file có trong thư mục, như bạn thấy tôi lưu kết quả vào trong mảng imgarr_fullpath
# tôi đặt tên như vậy vì file trả về đường dẫn full tới ảnh
readonly upload_dir="/home/framgia/Pictures/upload"
imgarr_fullpath=( $(find ${upload_dir} -type f) )
# Do vậy tôi cần tách để chỉ lấy tên file - chính là ID của nhân viên mà không có đuôi mở rộng
imgarr=()
for ((i=0; i<${#imgarr_fullpath[@]}; i++));
do
 imgarr[$i]=$(basename ${imgarr_fullpath[$i]}) #basename trong shell trả về tên file với phần mở rộng
 imgarr[$i]=${imgarr[$i]%.*} #tiếp theo tôi tách nốt phần đuôi mở rộng để chỉ lấy tên
done
# Đoạn này tôi dùng vòng lặp duyệt tất cả phần tử của mảng chứa ID nhân viên để tạo 1 file chứa các câu lệnh UPDATE theo điều kiện ID nhân viên có trong danh sách
# Bạn hãy để ý tôi dùng hàm LOAD_FILE của SQL để đọc file ảnh với đường dẫn đầy đủ, bởi vì hình ảnh lưu trong DB ở dạng BLOB nên SET như các kiểu dữ liệu thông thường sẽ không thể được
for ((j=0; j<${#imgarr[@]}; j++));
do
 echo "UPDATE emp SET image = LOAD_FILE('${imgarr_fullpath[$j]}') WHERE emp_number = '${imgarr[$j]}';" >> update.sql
done
# Sau khi đã có file .sql của mình, tôi thực hiện connect tới DB với khai báo các thông tin cần thiết sau
host="127.0.0.1"
port="3306"
dbname="mydatabase"
user="root"
pass="123456"
# Dùng câu lệnh mysql như khi bạn dùng trong terminal để truy cập DB và thực thi các câu lệnh được chứa trong file .sql đã tạo
mysql -h$host -P$port -u$user -p$pass -D$dbname <update.sql

Những kiến thức ở bài này rất cơ bản về bash shell và xuất phát từ bài toán thực tế tôi gặp phải, có thể sẽ rất ít áp dụng trong thực tế nhưng tôi hy vọng đã cung cấp được cho các bạn một cái nhìn sơ lược về shell script 😃


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.