Thursday, November 21, 2024

[Bash] Tips and tricks

-

Đối với bất kỳ những ai đã và đang làm việc với hệ thống Linux thì chắc chắn đều thấy sự hiệu quả của nhứng script Bash Shell. Việc sử dụng script Bash shell giúp các công việc lặp đi lặp lại trên Linux được thực hiện hiệu quả, ít sai sót. Tuy nhiên để viết các script Bash shell ngắn, gọn và có thể tận dụng được trên nhiều môi trường thì chúng ta cần có những kiến thức và lượng thực hành nhất định.

Trong Bash shell, có một số biến đặc biệt được sử dụng để thực hiện các tác vụ nhất định. Dưới đây là một số biến đặc biệt quan trọng:

  • $0: Biến này chứa tên của chương trình hoặc script đang thực thi.
  • $1, $2, $3, … $n: Các biến này chứa các tham số được truyền vào cho script hoặc chương trình. $1 chứa tham số đầu tiên, $2 chứa tham số thứ hai và cứ tiếp tục.
  • $#: Biến này chứa số lượng các tham số được truyền vào cho script hoặc chương trình.
  • $*: Biến này chứa tất cả các tham số được truyền vào cho script hoặc chương trình, được phân tách bởi dấu cách.
  • $@: Biến này cũng chứa tất cả các tham số được truyền vào cho script hoặc chương trình, nhưng các tham số được giữ nguyên với các khoảng trắng ban đầu.
  • $?: Biến này chứa mã trả về của lệnh được thực thi gần nhất.
  • $$: Biến này chứa Process ID (PID) của shell hiện tại.
  • $!: Biến này chứa PID của lệnh nền cuối cùng được thực thi.

Những biến đặc biệt này được sử dụng rất phổ biến trong scripting shell để truy cập và thao tác các thông tin như tên chương trình, tham số, trạng thái trả về, v.v.

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

  • Xóa các dòng từ 5 đến 10 và dòng thứ 12. Thao tác này sẽ in kết quả ra màn hình và lưu kết quả vào cùng một file.
sed -e '5,10d;12d' file

Điều này sẽ lưu trữ file chưa sửa đổi dưới dạng file.bak và xóa các dòng đã cho.

sed -i.bak -e '5,10d;12d' file

Lưu ý: Số dòng bắt đầu là 1. Dòng đầu tiên của file sẽ là 1, không phải 0.

  • Tìm vị trí giá trị nằm ở dòng bao nhiêu.
cat connect-vps.txt | grep -ni '192.168.13.232'
cat connect-vps.txt | grep -ni '192.168.13.232' | awk -F: '{print $2" - Line number : "$1}'
cat connect-vps.txt | grep -ni '192.168.13.232' | awk -F: '{print $1}'
  • Lấy giá trị từ dòng số 3 trở xuống.
# Ví dụ lấy giá trị từ dòng số 3 trở xuống và dừng lại khi có giá trị
$ virsh list --all | awk 'FNR>='3'{print; exit}'
 12   kvm-k8s1   running

# Ví dụ lấy giá trị từ dòng số 3 trở xuống cho đến hết dòng, dòng cuối cùng là dòng trống 
$ virsh list --all | awk 'FNR>='3'{print;}'
 12   kvm-k8s1   running
 13   kvm-k8s2   running
 14   kvm-k8s3   running
       < dòng trống >

# Để xoá dòng trống cuối dùng, sử dụng head -n -1
$ virsh list --all | awk 'FNR>='3'{print;}' | head -n -1
 12   kvm-k8s1   running
 13   kvm-k8s2   running
 14   kvm-k8s3   running
  • Lấy giá trị ở trước hoặc sau dấu hai chấm.
$ echo 'Ha Dang Hoang:Devops' | cut -d':' -f1 

-> Output
Ha Dang Hoang
  • Xóa khoảng trắng.
$ echo 'Ha Dang Hoang:Devops' | sed 's: ::g' 

-> Output
HaDangHoang:Devops
  • Xóa xuống dòng.
echo -e 'Ha Dang Hoang\nDevops' | tr -d '\n'

-> Output
Ha Dang HoangDevops
  • Lấy địa chỉ IPv4.
$ echo 'ipaddr: 192.168.1.1/24' | grep -oE "\b([0-9]{1,3}\.){3}([0-9]{1,3})\b"

-> Output
192.168.1.1
  • Lấy địa chỉ Mac address.
ip a | grep -o -E '([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2}

-> Output
00:00:00:00:00:00
00:00:00:00:00:00
00:30:48:db:6c:5e
  • Ví dụ 1 vòng lặp for.
list_node=('192.168.13.206|master1' '192.168.13.207|master2')
for i in ${list_node[@]};do
    IFS='|' read ipaddr hostname <<< "$i"
    echo "$ipaddr $hostname"
done

-> Output
192.168.13.206 master1
192.168.13.207 master2
  • Xóa ký tự cuối cùng trong chuỗi.

$ fullname='Ha Dang Hoang'
fist_name=${fullname::-3}
echo $fist_name
-> Ha Dang Ho

$ fullname='Ha Dang Hoang'
fist_name=${fullname::2}
echo $fist_name
-> Ha
  • Vòng lặp for kết hợp với while.
#!/bin/bash
lsvps=('172.16.1.1/24|k8s-master|2|4|50|3' '172.16.1.1/24|k8s-worker|2|4|50|5' '172.16.1.1/24|k8s-lbl|2|4|50|1')

count=1 && for i in ${lsvps[@]};do
  IFS='|' read subnet vps_name vCPU vRAM vDisk sl <<< "$i"
  old_vps_name=$vps_name
  if [[ $old_vps_name == $vps_name ]];then count=1;fi
  while [ $count -le $sl ];do
    if [[ $sl == 1 ]];then
      echo $vps_name
    else
      echo $vps_name$count
    fi
    count=$(($count+1))
  done
done

-> OutPut
k8s-master1
k8s-master2
k8s-master3
k8s-worker1
k8s-worker2
k8s-worker3
k8s-worker4
k8s-worker5
k8s-lbl
  • Thêm user và thay đổi mật khẩu nhanh đối với Centos.
sudo useradd -m hoanghd
echo "Hoanghd164" | passwd --stdin hoanghd
  • Thêm user và thay đổi mật khẩu nhanh đối với Ubuntu.
sudo useradd -m hoanghd
echo "hoanghd" | hoanghd
  • Tính toán trong bash shell.
a=10
b=20
val=`expr $a + $b`
echo "a + b : $val"
  • Hoặc sử dụng cú pháp: $((…)) để tính toán.
a=10
b=20
val=$(( a + b ))
echo "a + b : $val"
  • So sánh trong bash.
“-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 ]]

Ví dụ:
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

#Kết quả
# 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 “$@” thì các tham số được xem như các biến riêng lẽ được đặt trong một mảng
# Using "$@":
my
name
is
Dang

Sự khác nhau giữa $* và $@

Về cơ bản thì $* và $@ 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.
  • $@: 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ụ:

#!/bin/bash
echo -e "Using "$*":"
for str in "$*"
do
  echo $str
done
echo -e "Using "$@":"
for str in "$@"
do
  echo $str
done

Mảng trong bash:

Cú pháp khai báo:

# <tên mảng>=([giatri1] [giatri2] ... [giatrin])

Ví dụ:
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"`)
  • Chuyển đổi từ hàng dọc thành hàng ngang và chèn thêm ký tự ở giữa.
echo "ha
dang
hoang" | sed 's/$//g' | tr -d '\n'

-> Output: ha-dang-hoang

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.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories