Các toán tử cơ bản, cấu trúc so sánh và mảng trong Bash

Xin chào các bạn, trong bài trước của series chúng ta đã làm rõ 1 số khái niệm về Bash và Shell. Trong series này, chúng ta sẽ đi sâu vào ngôn ngữ thông dụng nhất đang được sử dụng hiện tại là Bash. Bài hôm nay, chúng ta hãy cùng tìm hiểu về các toán tử cơ bản và so sánh các toán tử trong ngôn ngữ Bash.

Các biến đặc biệt trong Bash:

Trong Bash có 1 số biến đặc biệt như sau:

Biến Đặc Biệt Ý Nghĩa
$0 Chứa tên file script hiện tại
$n ($1, $2,..$n) Với n có thể là 1, 2,… tương ứng với các tham số đưa vào khi gọi script.

VD: ./test.sh so01 so02 so03

Như VD trên thì có 3 tham số được đưa vào là so01, so02, so03 tương ứng với $1, $2, $3.

$# Tổng số tham số đã cung cấp cho script. Dựa theo VD trên thì kết quả khi dùng biến này sẽ ra 3
$* Chứa tất cả tham số được đưa vào script. Nếu script nhận 3 tham số thì giá trị sẽ là $1 $2 $3 khi sử dụng
[email protected] Chứa tất cả tham số nhưng phân biệt thành những tham số riêng lẽ không như $* được đưa vào script. Nếu script nhận 3 tham số thì giá trị sẽ là $1 $2 $3 khi sử dụng
$? Trạng thái thoát ra của lệnh trước được chạy (thường la 0 đại diện cho lệnh trước chạy thành công, khác 0 là failed) Max range [0 – 255]
$$ Số tiến trình của shell hiện tại. Đối với Shell script đây là số ProcessID mà chúng đang chạy
$! Số tiến trình của lệnh background trước

 

Sự khác nhau giữa $* và [email protected]

Về cơ bản thì $* và [email protected] giống nhau khi sử dụng với biến đơn không đặt trong dấu “”. Khi đặt vào trong dấu nháy kép “” thì:

  • $*: Các tham số đưa vào sẽ được nối thành một chuỗi và được phân cách nhau bởi dấu cách.
  • [email protected]: Các tham số đưa vào sẽ được phân biệt một cách riêng lẽ từng tham số một.

Ví dụ khác nhau giữa $* và [email protected]:

#!/bin/bash

echo -e "Using "$*":"
for str in "$*"
do
  echo $str

done


echo -e "Using "[email protected]":"
for str in "[email protected]"
do
  echo $str
done

Kết quả khi chạy script ./test.sh my name is Dang là:

# Khi sử dụng “$*” thì các tham số được xem là một chuỗi phân biệt bởi khoảng trắng
# Using "$*":

my name is Dang

# Khi sử dụng “[email protected]” thì các tham số được xem như các biến riêng lẽ được đặt trong một mảng
# Using "[email protected]":

my
name
is
Dang

Các toán tử cơ bản:

  • Về cơ bản thì shell linux sử dụng các toán tử cơ bản như các ngôn ngữ lập trình khác như C/C++, Java,… Tuy nhiên với toán tử nhân khi sử dụng nên dùng \* để phân biệt với lệnh bất kỳ * của linux
  • Sử dụng cú pháp: expr op1 <phép tính> op2 (để trong 2 dấu “ ` ” không phải nháy đơn đây là để thực thi lệnh shell expr)
echo `expr 2 + 3`  #sẽ hiển thị kết quả là 5

echo `expr 2 \* 3’ #kết quả là 2 * 3 = 6
  • Sử dụng cú pháp let “phép tính”
let “a=$a+3”

let ”c=$a*$b”

 Ngoài ra let có thể sử dụng các toán tử +=, -=, =+, =- khá tương tự như trong C/C++ và java

  • Sử dụng cú pháp: $((…)) 
Z=$(($a + $b))

Z=$(($a * $b))

Kiểm tra điều kiện (condition):

  • Có cấu trúc [ condition ] 
  • Trả về giá trị có kiểu boolean (true / false)

ví dụ:

if [4 -eq 2];
then
    echo True;
else
    echo False;
fi

# False

So sánh số học:

  • “-eq” => Bằng nhau (Equal)
  • “-ne” => Không bằng nhau (Not equal)
  • “-lt” => Nhỏ hơn (Less than)
  • “-gt” => Lớn hơn (Greater than)
  • “-le” => Nhỏ hơn hoặc bằng (Less or equal)
  • “-ge” => Lớn hơn hoặc bằng (Greater or equal)
  • “-o”  => OR
  • “-a”  => AND

Ví dụ:

var=10 
[ $var -eq 0 ] => FALSE

So sánh chuỗi:

  • [[ $str1 = $str2 ]] hoặc [[ $str1 == $str2 ]]
  • [[ $str1 != $str2 ]]
  • [[ $str1 > $str2 ]]
  • [[ $str1 < $str2 ]]
str1="HELLO" 
str2="Hello" 
[[ $str1 > $str2 ]] =>TRUE
  • [[ -z $str1 ]]: Trả về TRUE nếu $str1 là 1 chuỗi rỗng. FALSE nếu ngược lại
  • [[ -n $str1 ]]: Trả về TRUE nếu $str1 là 1 chuỗi khác rỗng. FALSE nếu ngược lại.

Kiểm tra tập tin:

  • [[ -f FILE ]] : Trả về TRUE nếu file_var là 1 tập tin.
  • [[ -x FILE ]]: Trả về TRUE nếu var là tập tin và có quyền thực thi (executable).
  • [[ -d FILE ]] : Trả về TRUE nếu var là 1 thư mục.
  • [[ -e FILE ]] : Trả về TRUE nếu var tồn tại.
  • [[ -w FILE ]] : Trả về TRUE nếu var là 1 tập tin và có quyền ghi (writable).
  • [[ -r FILE ]] : Trả về TRUE nếu var là 1 tập tin và có quyền đọc (readable).
  • [[ -h FILE ]] : Trả về TRUE nếu var là 1 liên kết mềm (symlink).
  • [[ -s FILE ]] : Trả về TRUE nếu file có dung lượng lớn hơn 0 bytes. 
  • [[ FILE1 -ef FILE2 ]] : Trả về TRUE nếu cùng là 1 file.

ví dụ:

fpath="/etc/passwd"

if [ -e $fpath ]; then
    echo File exists;
else
    echo Does not exist;
fi

mảng trong bash:

  • Cú pháp khai báo:
# <tên mảng>=([giatri1] [giatri2] ... [giatrin])

list=(coffee water beer)
  • Lấy số lượng phần tử của mảng:
# ${#<tên mảng>[@]}

elements = ${#list[@]}
  • Truy xuất phần tử trong mảng:
# ${<tên mảng>[<giá trị>]
# sẽ cho giá trị đầu tiên trong mảng là coffee

${list[0]}
  • Tìm và thay thế phần tử trong mảng:
# ${<tên mảng>[@]/pt_find/pt_replace}
# lệnh này sẽ thay thế phần tử có giá trị coffee thành milk

${list[@]/coffee/milk}
  • In tất cả phần tử, phân cách bởi space
# ${<tên mảng>[@]}

${Fruits[@]}
  • Độ dài chuỗi của phần tử đầu tiên
# ${#<tên mảng>}

${#Fruits}
  • Độ dài chuỗi của phần tử n
# ${#<tên mảng>[vị trí n]}

${#Fruits[3]}
  • Lấy mảng con từ vị trí n với m phần tử.
# ${<tên mảng>[@]:<vị trí n>:}

# lấy từ vị trí 3, 2 phần tử
${Fruits[@]:3:2}
  • Thêm phần tử (Push).
# <tên mảng> =("${<tên mảng>[@]}" "[phần tử cần thêm]")

Fruits=("${Fruits[@]}" "Watermelon")

# <tên mảng>+=('[phần tử cần thêm]')

Fruits+=('Watermelon')
  • Xoá phần tử:
# Remove by regex match
# <tên mảng>=( ${<tên mảng>[@]/[regex match]}/ )

Fruits=( ${Fruits[@]/Ap*/} )

# Xoá phần tử ở vị trí n
# unset <tên mảng>[vị trí n]

unset Fruits[2]
  • Tạo bản sao
# <tên bản sao>=("${<tên mảng cần sao>[@]")

list=("1" "2" "3")
list2=("${list[@]}")

echo ${list2[@]}
# 1 2 3
  • Nối mảng:
list=("1" "2" "3")
list2=("4" "5" "6")

# <tên mảng mới>=("${<tên mảng 1>[@]}" "${<tên mảng 2>[@]}")
list3=("${list[@]}" "${list2[@]}")

echo ${list3[@]}
# 1 2 3 4 5 6
  • Tạo mảng từ file
# <tên mảng>=(`cat "đường dẫn file"`)

lines=(`cat "logfile"`)

Qua bài viết trên, chúng ta đã nắm được các toán tử cơ bản trong bash, cách thực hiện các phép tính toán, các phép so sánh số học và chuỗi, kiểu dữ liệu mảng (array). Sau bài viết, các bạn hãy thực hành thêm về các toán tử cơ bản trên để nắm chắc về chúng nhé vì chúng sẽ hổ trợ rất nhiều cho chúng ta trong việc học tập lập trình ngôn ngữ Bash sau này đó. Bài viết sau, mình sẽ cùng các bạn tìm hiểu về các cấu trúc điều khiển và các toán tử xử lý chuỗi trong ngôn ngữ Bash.

Shell là gì ? Sự khác nhau giữa SH và Bash

7Host bảo vệ dữ liệu của khách hàng như thế nào?

INODE là gì?

Hướng dẫn tắt chế độ Maintenance trên WordPress

Hướng dẫn cài đặt nhanh NodeJS trên CentOS 7

Phân biệt IMAP và POP

Hướng Dẫn Cài Đặt CentOS 8 Toàn Tập

Hướng dẫn cài Apache Spark trên Ubuntu 20.04 LTS