Frankiie's Corner

Đơn giản hóa, và Tối ưu Command Line bằng Aliasing trong Linux

03 Mar 2023

Với những ai yêu thích sử dụng terminal và command line để thao tác công việc thường ngày thay vì sử dụng UI với nhứng cú click chuột lách ta lách tách thì chắc hẳn sẽ yêu thích hệ điều hành Linux (Linux kernel). và dĩ nhiên mình cũng là một trong số đó 😊. TUY NHIÊN có khi nào bạn cảm thấy việc gõ đi gõ lại một command line dài dài, thêm vào đó là những option them kèm khó nhớ. Ví dụ như:

Gửi một HTTP request sử dụng CURL:

curl -verbose -X POST "http://localhost:xxxx" -Header "Content-Type: application/json" --data '{"name":"foo"}'

Hay là, in ra log của project có git với một format cụ thể:

git log --pretty=format:"%h - %an, %ar : %s"

Thành thật, dù có là người yêu thích gõ phím hay sử dụng terminal đi chăng nữa thì chắc hẳn chúng ta cũng cảm thấy khá tốn thời gian, và phiền toái mỗi khi phải sử dụng những dòng lệnh dài như trên 🥲.

Mình biết sẽ có bạn bảo có thể search lại những command đã dùng bằng Ctrl + R. Nhưng giả sử bạn dùng một command nhưng với option khác thì gần như cũng phải sửa lại thui à. Ví dụ như lệnh CURL bên trên:

$ curl -verbose -X PUT “http://localhost:xxxx” -Header “Content-Type: application/json” –data ‘{“age”:”18”}’

Trong bài viết này, mình sẽ chia sẻ cách mình sử dụng Aliasing, và Function trong ngôn ngữ lập trình Bash Script để đơn giản hóa, và tối ưu các command line thường dùng ở môi trường Linux nhé.

Prerequisites

  • Linux (Ubuntu, Debian)
    • WSL, WSL2
  • Biết dùng Command Line và Terminal cơ bản

Getting Started

Trước khi bắt đầu, ta cần biết đôi chút về Aliasing.

Trong Linux, alias là một shortcut, có thể dùng để gán vào một lệnh cụ thể giúp làm ngắn gọn, và đơn giản hóa lệnh đó. Nó giúp chúng ta có thể thoải mái tùy chỉnh và gán alias cho lệnh bất kì. khi sử dụng những lệnh được gán với alias, chúng ta chỉ cần gọi tên của alias đã được gán với lệnh.

Bước đầu tiên, mở .bashrc file để thao tác:

$ vim ~/.bashrc

Nếu bạn sử dụng zsh thay vì bashrc (mình sử dụng zsh):

$ vim ~/.zshrc

Aliasing

#Network

  • In ra tất cả các TCP ports đang được active, sau đó in ra các thống kê liên quan đến chúng:
alias tcp_active='echo "> Here are TCP Listening ports:" && netstat -at && echo "\n> Statistics for all ports:" && netstat -st'

Sử dụng như sau, ra terminal bạn chỉ cần gõ tên của alias đã được gán:

$ tcp_active

Kết quả minh họa

tcp active

  • In ra tất cả các UDP ports đang được active, sau đó in ra các thống kê liên quan đến chúng:
alias udp_active='echo "> Here are UDP Listening ports:" && netstat -au && echo "\n> Statistics for all ports:" && netstat -su'

Sử dụng:

$ udp_active

Kết quả minh họa:

image

  • In ra tất cả các UNIX ports đang được active, sau đó in ra các thống kê liên quan đến chúng:
alias unix_active='echo "> Here are UNIX Listening ports:\n" && echo "\n> Statistics for all ports:" && netstat -lx'
  • In ra tất cả các chương trình đang được active, sau đó in ra các thống kê liên quan đến chúng:
alias active_all='echo "> Here are all listening programs:\n" && netstat -lp'

# thay vì `netstat`, bạn có thể sử dụng `lsof`
alias active_port='sudo lsof -i -P -n'

#System, và Files

  • In ra 30 files có kích thước lớn nhất ở directory hiện tại:
alias largest_files='du -h -x -s -- * | sort -r -h | head -30'
  • In ra tất cả các user hiện tại trong hệ thống:
alias sys_users='cut -d: -f1 /etc/passwd | sort'
  • Chỉ cần ấn c là xóa luôn cái màn hình terminal hiện tại:
alias c='clear'

#Git

  • In ra lịch sử Git log của project với format <commit_hash> <origin> <commit_message> trên cùng một dòng:
alias glo='git log --oneline'
  • In ra lịch sử Git log của project với format cụ thể:
alias glp='git log --pretty=format:"%h - %an, %ar : %s"'
  • Tạo một Git branch mới, rồi nhảy qua branch mới tạo đó:
alias gb='git checkout -b'

Làm việc với Function

Như ta có thể thấy Alias đơn giản, ngắn gọn và có thể giải quyết được rất nhiều những lệnh cơ bản đúng không. Tuy nhiên sẽ có những lúc chúng ta cần xử lí những task phức tạp cần dùng những câu lệnh chứa một hoặc nhiều tham số khác nhau. Lúc này, Function sẽ giúp chúng ta giải quyết vấn đề đó.

#System, và Files

  • In ra PID (Process ID) của một port cụ thể:
pid() {
  local port=$1
  local process_id=$(lsof -t -i :${port})
  echo $process_id
}

Cách dùng:

$ pid <port>

Kết quả mình họa:

pid

  • In ra thông tin chi tiết của process đang chạy trên một port cụ thể dưới format: COMMAND PID FD TYPE
p_info() {
  local port=$1
  local pInfo=$(lsof -i :${port} | awk '{ print $1" "$2" "$3" "$4" "$5 }')

  # Lưu ý: nếu muốn bỏ cái cái tiêu đề format `COMMAND` `PID` `FD` `TYPE`,
  # mà chỉ lấy giá trị, thì ta cần chèn thêm lệnh `tail -n +2` như sau:
  # local pInfo=$(lsof -i :${port} | tail -n +2 | awk '{ print $1" "$2" "$3" "$4" "$5 }') # get three columns

  if [ -n "$pInfo" ]; then
    echo $pInfo
  else
	  echo "No process running on port $port"
	  return 1
  fi
}

Cách dùng:

$ p_info <port>

Kết quả minh họa:

p_info

  • Tìm process đang được active thông qua một port cụ thể, rồi kill nó:
kill_p() {
  local port=$1
  # tách PID (Process ID) từ process mà ta muốn kill
  local pid=$(lsof -t -i :${port})

  # nếu PID không rỗng
  if [ -n "$pid" ]; then
    echo "Process running on port $port with PID $pid. Killing process..."
    kill $pid

    # kiểm tra xem nó đã bị kill chưa
    sleep 1
    if kill -0 $pid >/dev/null 2>&1; then
      echo "ERROR: Failed to kill process with ID $pid."
      return 1
    else
      echo "OK: Process with ID $pid was killed successfully."
    fi
  else
    echo "No process found running on port $port."
  fi
}

Cách dùng:

$ kill_p <port>
  • In ra lịch sử sử dụng của một task cụ thể.
hg() {
  local task=$1

  if [ -n "$task" ]; then
    history | grep "$task";
  else
    echo "EXCEPTION: history of what?"
    echo "E.g. history vim"
  fi
}

Cách dùng:

$ hg <task>

Kết quả minh họa:

image

  • Hiển thị nội dung của directory ở định dạng giống như những nhánh cây. (Giống tree command nhưng nhìn gọn hơn về cấu trúc).
dir_tree() {
  find $1 -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
}

Cách dùng:

$ dir_tree

Kết quả minh họa:

dir_tree

  • Tải một package nào đó:
apt_i() {
	local pkg=$1
	if [ -n "$pkg" ]; then
		command sudo apt update && sudo apt-get install $pkg
	else
		echo "EXCEPTION: provide <package> to complete instalation."
		return 1
	fi
}

Cách dùng:

$ apt_i <package_name>
  • Xóa đi một package nào đó:
apt_rm() {
	local pkg=$1
	if [ -n "$pkg" ]; then
		command sudo apt remove $pkg
	else
		echo "EXCEPTION: invalid arguments."
		echo "E.g. apt_rm <package_name>"
	fi
}

Cách dùng:

$ apt_rm <package_name>
  • Chuyển file từ host machine tới remote server sử dụng giao thức SCP.
scp_do() {
  local file=$1
  local body=$2

  if [[ -n "$file" && -n "$body"]]; then
    command scp -v $file $body
  else
    echo "EXCEPTION: invalid arguments."
    echo "E.g. $ scp_do -v <file> <user>@<ip>:/destination_path"
  fi
}

Cách dùng:

$ scp_do -v <file> <user>@<ip> <destination_path>
  • Thực thi gửi HTTP request sử dụng CURL
curl_do() {
  local method=$1
  local url=$2
  local body=$3

  case $method in
    "GET") # nếu request là GET
      command curl -v "http://$url" | json ;;

    "DELETE") # nếu request là DELETE
      command curl -v -X $method "http://$url" | json ;;

    "PUT") # nếu request là PUT
      command curl -v -X $method "http://$url" -H "Content-Type: application/json" -d $body | json ;;

    "POST") # nếu request là POST
      command curl -v -X $method "http://$url" -H "Content-Type: application/json" -d $body | json ;;

    *)
      echo "Exception: invalid HTTP request method: $method"
      echo "E.g. curl_do <GET | DELETE | PUT | POST> <url> <body>"
      return 1 ;;
  esac
}

Cách dùng:

# GET
$ curl_do GET localhost:xxxx

# POST
$ curl_do POST localhost:xxxx '{"username": "foo", "password":"bar"}'

# PUT, DELETE tương tự nha...
  • Tìm file hoặc directory ở một ví trí cụ thể bằng pattern, rồi xóa nó:
find_rm() {
  local location=$1
  local type=$2
  local pattern=$3

  case $type in
    "f") # nếu là file
        find $location -type $type -name "*$pattern*" -delete ;;

    "d") # nếu là directory
        find $location -type $type -name "*$pattern*" -delete ;;

    *) # không hợp lệ
      echo "EXCEPTION: invalid type (only accept `f` or `d`)"
      echo "E.g. find_rm <location> <f | d> <pattern>"
      return 1
    ;;
  esac
}

Cách dùng:

# tìm file và xóa
$ find_rm <location_muốn_tìm> f <pattern>

# tìm directory và xóa
$ find_rm <location_muốn_tìm> d <pattern>

#Git

  • Tạo git cho project:
git_init() {
  local dir=$1

  if [ -n "$dir" ]; then
    # tạo directory -> chuyển hướng sang directory -> in tên, và vị trí của directory hiện tại -> tạo git
    mkdir $dir && cd $dir && pwd && git init
    touch README.md .gitignore LICENSE
  else
    echo "EXCEPTION: invalid arguments. Please provide a directory name.";
    echo "E.g. git_init test-dir"
    return 1
  fi
}
  • Kiểm tra xem hiện tại đang ở branch nào trong project:
git_br() {
    if [ -d .git ] ; then
        printf "%s" "> You are at branch: $(git branch 2> /dev/null | awk '/\*/{print $2}'))";
    fi
}
  • Đẩy local branch lên remote origin:
git_po() {
  local origin=$1

  git push origin -u $origin
}
  • Thêm Git url vào remote origin:
git_rao() {
  local url=$1

  git remote add origin $url
}
  • Chỉ định initial/default branch cho repository:
git_br() {
  local br=$1

  git branch -M $br
}
  • Viết commit
git_cm() {
  local msg=$1
  git commit -m $msg
}
  • Xóa file ở Git remote repository nhưng vẫn giữ lại ở local:
git_rm_local() {
  local file=$1
  local msg=$2
  local br=$3

  # nếu file không rỗng, xóa nó ở remote (và vẫn giữ nó lại ở local)
  if [ -n "$file" ]; then
    git rm --cached $file
  else
    echo "EXCEPTION: invalid arguments. <file> should not be null."
    return 1
  fi

  # nếu tham số `msg` không rỗng, viết commmit cho nó
  if [ -n "$msg" ]; then
    git commit -m $msg
  fi

  # nếu tham số `msg` và `br` không rỗng, đẩy nó lên remote origin `br`
  if [[ -n "$msg" && -n "$br" ]]; then
    git push origin $3
  fi
}

Cách dùng:

# chỉ xóa file, và không commit hay push lên remote
$ git_rm_local <file>

# chỉ xóa file, và commit, không push lên remote
$ git_rm_local <file> <message>

# xóa file, commit, và push lên remote
$ git_rm_local <file> <message> <origin_branch>
  • Chắc hản có bạn sẽ nghĩ, từ đầu tới giờ, cũng kha khá nhiều lệnh rồi, sao mà nhớ hết, rồi cũng lại phải Google thôi…?? Hmm, ta lưu các lệnh trên vào một alias cụ thể, nếu quên hoặc không nhớ ta chỉ cần gọi alias đó ra để xem là được nha.
alias my_aliases='echo "
  $ tcp_active: Giải thích về lệnh này. Cách dùng ..... \n
  $ udp_active: Giải thích về lệnh này. Cách dùng ..... \n
  ....
  ....
  ....
" '

LƯU Ý: Những thứ liên quan đến code, tên hàm, biến, tham số, cách gán, logic… tất cả được đặt theo quan điểm của mình, chứ không theo một Naming convention, hay Clean code cụ thể nào cả. Mình cố gắng trình bày đầy đủ nhất có thể cho người mới học có thẻ hiểu được. Bạn có thể copy về rồi trình bày theo cách của riêng bạn nha.

Conclusion

Trên là những thứ mình tông hợp lại để phục vụ công việc thường ngày trong môi trường Linux. Nó giúp mình tối ưu, và tiết kiệp kha khá thời gian gõ phím và Google để tìm lại những lệnh cụ thể đã dùng. Hi vọng bạn tìm được điều gì đó hữu ích trong bài viết này. Chúc bạn học tập thành công.

Mình viết blog để tổng hợp lại những gì mình đã học, và cũng như học cách trình bày sao cho người khác có thể hiểu được, nên bài viết có thể tồn tại bug hoặc chưa hoàn thiện đâu đó. Nếu có gì liên quan đến bài viết, cần giúp debug, etc. thì đừng ngần ngại mà hãy cứ nhắn tin cho mình qua Facebook nha.