Linux - Scripting (Part 2)

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

QUẢN LÝ LINUX SERVER

SCRIPTING (PART 2)
Author: Nhã Phạm

OVERVIEW
Trong phần 1 của scripting, mọi người đã được làm quen cơ bản về viết một file script, các quy tắc cơ bản trong
scripting.

Phần 2 này mình sẽ giúp bạn một số cách dùng thường nhất với các xử lý thông tin trong scritp, tuy nhiên đây cũng
chỉ là những phần cơ bản, script sẽ tùy thuộc vào từng nhu cầu cụ thể mà sẽ có cách xử lý khác nhau.

Tham số
Đối với mình thì thường dùng tham số đi kèm chung với scritp vì nhiều lúc 1 script của mình sẽ xử lý nhiều liều lần
với các tác vụ khác nhau.

Cơ bản thì các tham số được gọi sau script như phần 1 đã đề cập.
# myscript.sh <tham số 1> <tham số 2> <tham số n>

Các tham số chạy kèm với script sẽ được định nghĩa như sau.

+ Biến $0 hoặc ${0} là tên của script.


+ Biến từ $1 hoặc ${1} trở về sau là các tham số được đi kèm với script.

Ta có VD như sau.
# myscript.sh 1 2 3

Thì trong script các biến sẽ được định nghĩa như sau.

• $0 có giá trị là myscript.sh


• $1 có giá trị là 1
• $2 có giá trị là 2
• $3 có giá trị là 3

Sau đó, ta sẽ xử lý các biến.


#!/bin/bash

RESULT=$1+$2+$3
echo $RESULT

Và sau khi chạy script thì kết quả trả về trên màn hình sẽ là 6

Tuy nhiên, trên thực tế là bạn sẽ bỏ vào chuỗi để xử lý chứ không phải con số, ví dụ ta có bài toán như sau.

+ Viết script để encrypt và decrypt một file nào đó.

Với bài toán trên thì mình sẽ cần có 2 tham số.

+ Tham số đầu tiên sẽ là encrypt hoặc decrypt, ta sẽ dùng condition statement để xác định mình cần encrypt
hay decrypt.
+ Tham số thứ 2 là tên file và vị trí file.

1
Ta sẽ điền tham số theo script như sau.
# myscript.sh <dec|enc> <file cần encrypt hoặc decrypt>

Ở đây, dec à decrypt và enc là encrypt. Trong script sẽ như thế này:
#!/bin/bash
if [ “$1” == “enc” ]; then
openssl enc -aes256 -pbkdf2 -k '12345678' -in $2 -out $2.enc
rm -f $2 # xóa file sau khi mã hóa
elif [ “$1” == “dec” ]; then
openssl enc -aes256 -pbkdf2 -k '12345678' -in $2.enc -out $2
rm -f $2.enc # xóa file encrypt sau khi đã decrypt
else
echo “file not found”
fi

Hoặc ta cũng có thể viết bằng cách sử dụng “CASE” statement như sau.
#!/bin/bash
case “$1” in
“enc”)
openssl enc -aes256 -pbkdf2 -k '12345678' -in $2 -out $2.enc
rm -f $2 # xóa file sau khi mã hóa
;;
“dec”)
openssl enc -aes256 -pbkdf2 -k '12345678' -in $2.enc -out $2
rm -f $2.enc # xóa file encrypt sau khi đã decrypt
;;
*)
echo “file not found”
;;
esac

Trên đây là cách sử dụng tham số kè theo đơn giản nhất, trên thực tế thì sẽ có nhiều cách hơn để áp tham số vào
bên trong script. Bạn có thể bỏ thêm thời gian đển nghiên cứu.

Condition statement.
Như đã nói ở phần 1, đối với Sysadmin, sử dụng If sẽ là rất nhiều vì chúng ta cần phải kiểm tra mọi dữ liệu có thể
có để tránh trường hợp bị lỗi, vì khi bị lỗi script sẽ tự thoát ra khỏi môi truòng đang chạy. Dưới đây là các kiểm tra
thường hay xài nhất đối với sysadmin.

Lưu ý: phần kiểm tra thường chỉ có true hoặc false tức là có hay không có nên mình sẽ sử dụng IF…ELSE làm
statement chính, nếu như kết quả cần kiểm tra có nhiều hơn có hay không có thì mình sẽ khuyến cáo sử dụng CASE

Kiểm tra sự tồn tại của file.


Thông thường sau khi hoàn thành script thì mình muốn lưu thông tin sau khi xử lý vào 1 file gọi là log để ghi lại bằng
chứng, lỗi và thời gian thực hiện, cách này sẽ giúp mình tránh bị lỗi về sau. Thành ra, trước khi ghi data vào file log,
mình phải kiểm tra xem file đó có tồn tại để mà ghi hay không.

Ngoài ra khi bạn muốn đọc dữ liệu từ file text nào đó, bạn cũng phải đảm bảo là file đó có tồn tại hay không thì mới
bắt đầu đọc.

Việc kiểm tra thì chỉ cần dùng IF…ELSE để kiểm tra mà thôi.
if [ -f <vị trí file> ]; then
<câu lệnh thực thi nếu file có tồn tại>
else
<câu lệnh thực thi nếu file không tồn tại>
fi

2
Chú ý, đối với việc kiểm tra file đó không tồn tại thì ta chi cần thêm dấu “!” vào phía trước ký tự kiểm tra, nó sẽ như
thế này:
if [ ! -f <vị trí file> ]; then
<câu lệnh nếu file không tồn tại>
fi

VD, kiểm tra có tồn tại file myscript.log hay không, nếu không thì tạo mới.
#!/bin/bash

LOG=/var/log/myscipt.log
if [ ! -f ${LOG} ]; then
touch ${LOG}
fi

Ngoài ra bạn cũng có thể dùng câu rút gọn như sau.
[ ! -f $LOG ] && touch $LOG

Lưu ý: Nếu bạn không kiểm tra mà xử lý luôn trên file thì có thể dẫn đến việc script sẽ stop vì nó ko tìm thấy được
file mà bạn cần xử lý.

Kiểm tra sự tồn tại của directory (folder).


Việc kiểm tra directory là nên có vì mình phải đảm bảo là chỗ chứa các file lưu hoặc vị trí mà bạn sẽ lưu thông tin
vào đó luôn tồn tại, nếu không thì script sẽ dừng và báo lỗi.

Tương tự với việc tìm file như VD trên, ta chỉ thay thể ký tự “f” bằng “d”, nó sẽ như phía dưới.
if [ -d <vị trí của directory> ]; then
<câu lệnh khi có directory có tồn tại>
else
<câu lệnh khi không có directory tồn tại>
fi

Giờ chỉ cần thêm ký tự “!” để kêu hàm trả về true khi directory không tồn tại, VD: Tạo directory “/tmp/test” nếu
không có sẵn
#!/bin/bash

TEST_PATH=/tmp/test
if [ ! -d ${TEST_PATH} ]; then
mkdir ${TEST_PATH}
fi

LƯU Ý: tránh tuyệt đối đặt tên biến là “PATH” nếu không thì nó sẽ ảnh hưởng đến các bin và sbin PATH, vì PATH là
biến lưu environment mặc định. Bạn có thể thử bằng câu lệnh sau.
# echo $PATH

Kết quả trả về sẽ là các environment đang có hiện tại.

Kiểm tra sự tồn tại của biến.


Biến trong script thường được gán từ đầu hoặc có thể được gán bởi kết quả trả về của của câu lệnh, VD:
RESULT=$(pwd)
echo $RESULT

Dòng trên trả về vị trí directory mà bạn đang đứng.

Tuy nhiên ở 1 số trường hợp thì câu lệnh kết quả trả về là “null”, điều sẽ ảnh hưởng đển script, thế nên ta nên kiểm
tra xem biến có giá trị gì hay không.

3
Lưu ý: Trong dev giá trị 0 vẫn là 1 giá trị, “null” đồng nghĩa là không có giá trị.

Để kiểm tra biến ta dùng IF như sau:


if [ -v “$<tên biến>” ]; then
<câu lệnh thực thi nếu biến không có giá trị>
fi

VD, ta kết hợp với câu lệnh lấy vị trí hiện tại như trên:
#!/bin/bash

RESULT=$(pwd)
if [ -v “$RESULT” ]; then
echo “Path is not exist!”
else
echo $RESULT
fi

Với biến có giá trị rồi thì ta chỉ cần dùng so sánh giữa Biến với giá trị.
if [ $<tên biến> <so sánh> <giá trị muốn so sánh> ]; then
<câu lệnh thực hiện nếu kết quả là TRUE>
fi

Trên là những gì mà bạn cần quan tâm khi so sánh biến hoặc kiểm tra biến.

So sánh chuỗi ký tự.


Phần lớn trong scripting, chúng ta thường xử lý trên chuỗi dữ liệu mà câu lệnh trả về nên việc so sánh các chuỗi dữ
liệu xem có khớp với cái chúng ta đang cần hay không.

Cách đơn giản nhất là tìm các key words trong chuỗi ký tự, VD: Ta có 1 chuỗi 'GNU/Linux is an operating system'
và dùng keyword là ‘Linux’, vậy ta sẽ so sánh xem trong chuỗi có keyword hay không, nếu có thì trả về true, trong
script sẽ như sau.
#!/bin/bash

STR='GNU/Linux is an operating system'


SUB='Linux'

if [ "$STR" == *"$SUB"* ]; then


echo "It's there."
fi

Hay bạn cũng có thể dùng kỹ thuật REGEX.


#!/bin/bash

STR='GNU/Linux is an operating system'


SUB='Linux'

if [[ "$STR" =~ .*"$SUB".* ]]; then


echo "It's there."
fi

Vẫn có các cách khác nhau để tìm keyword trong chuỗi, bạn có thể Google để biết thêm chi tiết.

Tuy nhiên ta vẫn có cách ăn gian xíu, đó là thay vì so sánh chuỗi, ta sẽ chuyển nó về con số để dễ so sánh. Cụ thể
với VD trên, trước tiên ta chuyển thành số
# grep -e "$SUB" <<< "$STR" | wc -l

Giải thích:

4
1. grep -e sẽ tìm chuỗi SUB trong STR
2. wc -l sẽ đếm số dòng xuất hiện trên màn hình.

Như vậy, lệnh ‘grep -e "$SUB" <<< "$STR"’ sẽ xuất ra màn hình dòng STR vì nó có chứa SUB, sau đó wc sẽ đếm số
dòng xuất hiện, ở đây kết quả sẽ là 1. Thành ra nếu áp vào script thì nó sẽ như thế này.
#!/bin/bash

STR='GNU/Linux is an operating system'


SUB='Linux'

if [ $(grep -e "$SUB" <<< "$STR" | wc -l) -gt 0 ]; then


echo "It's there"
fi

Trên là cách mà bạn có thể so sánh 2 chuỗi với nhau, vẫn còn có những cách khác, cách thực thi thì còn tùy thuộc
vào ý tường của bạn.

Looping.
Đối với vòng lặp, mình thường sử dụng “For” khi liên quan tới các mảng có giá trị rõ ràng, vì nó sẽ ít bước hơn và
do mảng có kết thúc so với “While”, thế nên sử dụng vòng lặp nào thì tùy thuộc vào thói quen của bạn mà thôi.

Mảng dữ liệu (array)


Ở bài viết này mình đa phần xử lý các mảng được tạo ra do câu lệnh liệt kê nên sẽ sử dụng “For” làm vòng lặp
chính, bài toán cụ thể là liệt kê các file bên trong directory cụ thể, script nó sẽ như thế này:
#!/bin/bash

MY_PATH=/mnt/data
ITEMS=$(ls $MY_PATH)

for ITEM in $ITEMS; do


echo “File $ITEM is there”
done

Và để so sánh, cùng kết quả nhưng với với “While”.


#!/bin/bash

MY_PATH=/mnt/data
ITEMS=$(ls $MY_PATH)
i=0
LEN=${#ITEMS[@]}

while [ $i -lt $LEN ]; do


echo ${ITEMS[$i]}
let i++
done

Như bạn có thể thấy là “FOR” nó ngắn hơn và cũng dễ hiểu hơn. Chú ý là cái này tùy từng người sẽ thích chọn
cách sử dụng, mình không nói là phải theo khuôn khổ của mình.

Chuỗi trong file.


Đối với đọc data từ file thì ta có thể dùng cả 2 cách cũng được, cho “For”:
#!/bin/bash

FILE=/mnt/data/test.txt

for line in $(cat $FILE); do


5
echo $line
done

Còn đối với “While”, ta sẽ có như sau:


#!/bin/bash

FILE=/mnt/data/test.txt

while read line; do


echo $line
done < $FILE

Cả 2 cách trên đều có sự tương đồng nhất định, bạn thích cách nào thì chọn cách đó nha.

Ví dụ.
Yêu cầu
Script có khả năng mã hóa và giải mã trong thư mục /mnt/data thỏa các điều kiện sau.

+ Sử dụng khóa có chuỗi “abc@123”


+ File sau khi được mã hóa sẽ được lưu trong /mnt/data/enc
+ File sau khi được mã hóa phải có đuôi là enc
+ File không được mã hóa phải được xóa sau khi được mã hóa
+ File sau khi được giải mã thì được lưu tại /mnt/data/dec

Scripting.
#!/bin/bash

# Khai báo biến liên quan tới vị trí của directory


DATA_PATH=/mnt/data
ENC_PATH=$DATA_PATH/ENC
DEC_PATH=$DATA_PATH/DEC

# Gán tham số vào biến


FILE_NAME=$2
OPTS_INFO=$1

# Biến khác
LOG=$DATA_PATH/info.log
KEY="abc@123"

if [ ! -d $DATA_PATH ]; then
echo "Data directory is not exist, please make sure it's there"
exit 1 # Thoát script nếu directory không tồn tại.
fi

if [ -v $FILE_NAME) ]; then
echo "The file name must be defined"
exit 1 # Thoát script khi tham số thứ 2 không tồn tại.
fi

if [ ! -d $ENC_PATH ]; then
mkdir $ENC_PATH # Tạo encrypt directory nếu không có
fi

if [ ! -d $DEC_PATH ]; then
mkdir $DEC_PATH # Tạo decrypt directory nếu không có
fi

FILE_PATH=$(find $DATA_PATH -type f -name "$FILE_NAME*") # Kiếm file trong thư mục
/mnt/data

6
if [ -v $FILE_PATH ]; then
echo "The file did not exists, exit now"
exit 1 # Thoát script khi file không tồn tại trong /mnt/data
fi

if [ ! -f $LOG ]; then
touch $LOG # Tạo file log nếu như không tồn tại.
fi

case "$OPTS_INFO" in
"enc")
openssl enc -aes256 -pbkdf2 -k "$KEY" -in $FILE_PATH -out
$ENC_PATH/$FILE_NAME.enc # Thực hiện việc mã hóa
rm -f $FILE_PATH # Bỏ file cũ sau khi đã được mã hóa
echo "$FILE_NAME was encrypted" | tee -a $LOG > /dev/null # Ghi vào log
thông báo việc mã hóa đã thành công.
;;
"dec")
if [ "$FILE_PATH" != ".enc"]; then
FILE_PATH=$FILE_PATH.enc # Thêm ký tự vào FILE_NAME nếu như tên không
chứa ký tự .enc
fi
openssl enc -aes256 -pbkdf2 -k "$KEY" -in $FILE_PATH -out
$DEC_PATH/$FILE_NAME # Thực hiện việc giải mã và lưa vào thư mục dec.
echo "$FILE_NAME was decrypted" | tee -a $LOG > /dev/null # Ghi vào log việc
giải mã file.
;;
*)
echo "There is no options here." # Thông báo script không có tham số thứ 1
và thoát khỏi script.
exit 1
;;
esac

for line in $(cat $LOG); do


echo $line # Đọc log và xuất ra màn hình.
done
# Hoàn thành script.

Lưu ý: Bản script trên không hoàn toàn cover hết những bug mà nó sẽ gặp phải, script này chỉ có tác dụng cho bạn
nắm được cách thức hoạt động cơ bản của script.

Tổng kết.
Bài viết này mong có thể giúp bạn bắt đầu trong việc viết script, với mình thì scripting là không thể thiếu để mình
có thể dành thời gian làm những việc khác thay vì làm 1 việc nhiều lần.

NHÃ PHẠM

You might also like