Tự học Bash Scripting từ A-Z: Viết Bash Script đơn giản cho người mới

Tự học Bash Scripting từ A-Z: Viết Bash Script đơn giản cho người mới

Bash shell là công cụ dòng lệnh mạnh mẽ và linh hoạt bậc nhất trong hệ điều hành Linux, đồng thời cũng là một trong những lý do hàng đầu khiến các lập trình viên, quản trị hệ thống và người dùng kỹ thuật lựa chọn Linux để làm việc.

Với Bash, bạn có thể tương tác trực tiếp qua terminal, viết các Bash script để tự động hóa quy trình, tối ưu hiệu suất và giảm thiểu thao tác lặp đi lặp lại. Mặc dù với người mới, Bash scripting có thể trông khá phức tạp, nhưng trên thực tế, việc làm quen và bắt đầu viết script lại đơn giản hơn bạn nghĩ rất nhiều – đặc biệt khi bạn có hướng dẫn rõ ràng và ví dụ thực tế để làm theo.

Mục lục nội dung

Bash Script là gì?

Bash script là một tập tin văn bản chứa tập hợp các lệnh được viết dành riêng cho Bash shell – trình thông dịch dòng lệnh phổ biến trên hệ điều hành Unix-based như Linux và macOS. Bash script thường được sử dụng để tự động hóa các tác vụ lặp đi lặp lại, thực thi nhiều lệnh liên tiếp, hoặc xây dựng những chương trình đơn giản giúp tăng hiệu suất công việc.

Nếu bạn đã quen thao tác với dòng lệnh trên Linux, thì việc viết Bash script không quá xa lạ – bởi mọi lệnh trong Bash script đều có thể thực thi trực tiếp trên terminal. Trong bài hướng dẫn này, chúng tôi sẽ giới thiệu tới bạn các ví dụ thực tế, giúp bạn hình dung rõ cách vận hành của Bash script và cách khai thác hiệu quả các tính năng mạnh mẽ mà Bash mang lại.

Từ người mới đến chuyên gia chỉ cách nhau vài dòng lệnh! Bạn hoàn toàn có thể bắt đầu bằng việc sao chép các đoạn script mẫu vào hệ thống, thử chạy, tinh chỉnh, và luyện tập thường xuyên. Chúng tôi sẽ đồng hành cùng bạn trên từng bước để chinh phục thế giới Bash scripting. Hãy cùng bắt đầu hành trình tự động hóa với Bash!

Trong hướng dẫn Bash scripting này, bạn sẽ học:

  • Cách viết script “Hello World” đầu tiên
  • Cách truyền tham số vào Bash script
  • Cách sử dụng biến toàn cục và cục bộ trong Bash
  • Cách đọc input từ người dùng
  • Cách sử dụng và đọc mảng trong Bash
  • Cách so sánh số nguyên và chuỗi
  • Cách kiểm tra kiểu file trong Bash
  • Cách sử dụng vòng lặp for, while và until
  • Cách định nghĩa và sử dụng hàm trong Bash
  • Cách sử dụng câu lệnh if
  • Cách sử dụng câu lệnh case
  • Cách xử lý dấu nháy và ký tự đặc biệt trong Bash
  • Cách thực hiện phép tính số học trong Bash
  • Cách sử dụng tính năng redirection (chuyển hướng) trong Bash

Yêu cầu phần mềm và quy ước dòng lệnh linux

Danh mục Yêu cầu, quy ước hoặc phiên bản phần mềm sử dụng
Hệ thống Hệ điều hành Linux
Phần mềm Bash shell (được cài mặc định)
Khác Quyền truy cập root hoặc sử dụng sudo

Quy Ước:

  • # – yêu cầu thực thi lệnh với quyền root
  • $ – lệnh được chạy với quyền người dùng thông thường

Script Hello World trong Bash – Hướng dẫn Bash Scripting

1. Trước tiên, bạn cần xác định vị trí của trình thông dịch Bash trên hệ thống. Hãy nhập lệnh sau vào dòng lệnh terminal:

$ which bash
/bin/bash

Lệnh này sẽ cho bạn biết rằng Bash shell được lưu tại đường dẫn /bin/bash. Thông tin này sẽ rất quan trọng trong bước tiếp theo.

2. Tiếp theo, hãy mở trình soạn thảo văn bản yêu thích của bạn và tạo một file có tên là hello_world.sh. Ở đây, chúng ta sẽ sử dụng nano cho đơn giản:

$ nano hello_world.sh

3. Sao chép và dán đoạn mã dưới đây vào file vừa tạo, sau đó lưu lại:

#!/bin/bash
# declare STRING variable
STRING="Hello World"
# print variable on a screen
echo $STRING

SHEBANG là gì?

Tất cả các Bash script trong hướng dẫn này đều bắt đầu bằng shebang #!, đây không phải là một comment. Dòng đầu tiên khai báo interpreter (trình thông dịch) sẽ được dùng – trong trường hợp này là /bin/bash.

4. Tiếp theo, điều hướng tới thư mục chứa file hello_world.sh và cấp quyền thực thi cho script:

$ chmod +x hello_world.sh

5. Giờ bạn đã sẵn sàng để chạy script đầu tiên:

$ ./hello_world.sh

Output:

Hello World

Bạn vừa tạo, lưu và thực thi thành công một Bash script đơn giản!

Script Backup đơn giản với Bash

Khi bạn viết một Bash script, thực chất bạn chỉ đang gói gọn các lệnh mà bạn có thể chạy trực tiếp trên dòng lệnh. Ví dụ hoàn hảo:

#!/bin/bash
tar -czf myhome_directory.tar.gz /home/linuxconfig

Lệnh này sẽ tạo file nén .tar.gz chứa toàn bộ thư mục home của user linuxconfig. Bạn hoàn toàn có thể chạy trực tiếp lệnh tar này trên dòng lệnh.

Vậy lợi ích của việc viết script là gì?
Script giúp bạn không cần ghi nhớ lệnh dài dòng, có thể tái sử dụng dễ dàng, và thuận tiện để mở rộng logic về sau.

Biến Trong Bash Script

Ví dụ sau khai báo một biến $STRING đơn giản và in ra với lệnh echo:

#!/bin/bash
STRING="HELLO WORLD!!!"
echo $STRING

Kết quả khi chạy script:

$ ./hello_world.sh
HELLO WORLD!!!

Giờ ta quay lại ví dụ backup phía trên và dùng biến để đặt tên file backup, có đính kèm timestamp (dấu thời gian) bằng lệnh date:

#!/bin/bash
OF=myhome_directory_$(date +%Y%m%d).tar.gz
tar -czf $OF /home/linuxconfig

Chạy script:

$ ./backup.sh
$ ls
myhome_directory_$(date +20220209).tar.gz

File kết quả cho ta biết backup được thực hiện vào ngày 9/2/2022.

Biến Toàn Cục vs. Cục Bộ Trong Bash

Trong Bash scripting, biến toàn cục (global variable) là biến có thể được sử dụng ở bất kỳ đâu trong toàn bộ script. Ngược lại, biến cục bộ (local variable) chỉ có thể được sử dụng bên trong hàm (function) nơi nó được khai báo.

Hãy xem ví dụ dưới đây, trong đó chúng ta khai báo cả biến toàn cục và biến cục bộ. Một số chú thích đã được thêm vào trong script để giúp bạn dễ hiểu hơn.

#!/bin/bash
# Define bash global variable
# This variable is global and can be used anywhere in this bash script
VAR="global variable"

function bash {
# Define bash local variable
# This variable is local to bash function only
local VAR="local variable"
echo $VAR
}

echo $VAR
bash
# Note the bash global variable did not change
# "local" is bash reserved word
echo $VAR

Kết quả khi thực thi script này:

$ ./variables.sh
global variable
local variable
global variable

Truyền tham số vào Bash Script

Khi bạn chạy một Bash script, bạn có thể truyền tham số (argument) vào script ngay từ dòng lệnh. Xem ví dụ dưới đây:

#!/bin/bash
# use predefined variables to access passed arguments
#echo arguments to the shell
echo $1 $2 $3 ' -> echo $1 $2 $3'

# We can also store arguments from bash command line in special array
args=("$@")
#echo arguments to the shell
echo ${args[0]} ${args[1]} ${args[2]} ' -> args=("$@"); echo ${args[0]} ${args[1]} ${args[2]}'

#use $@ to print out all arguments at once
echo $@ ' -> echo $@'

# use $# variable to print out
# number of arguments passed to the bash script
echo Number of arguments passed: $# ' -> echo Number of arguments passed: $#'

Chạy thử script với 3 tham số:

$ ./arguments.sh Bash Scripting Tutorial

Kết quả:

Bash Scripting Tutorial  -> echo $1 $2 $3
Bash Scripting Tutorial  -> args=("$@"); echo ${args[0]} ${args[1]} ${args[2]}
Bash Scripting Tutorial  -> echo $@
Number of arguments passed: 3  -> echo Number of arguments passed: $#

Thực thi lệnh shell trong Bash

Cách tốt nhất để thực thi một lệnh shell riêng biệt bên trong Bash script là tạo một subshell bằng cú pháp $( ). Xem ví dụ dưới đây – chúng ta sử dụng echo để in kết quả của lệnh uname -o:

#!/bin/bash
# use a subshell $() to execute shell command
echo $(uname -o)
# executing bash command without subshell
echo uname -o

Lưu ý rằng ở dòng cuối cùng, chúng ta không đặt lệnh uname trong subshell, vì vậy Bash chỉ in ra văn bản thô “uname -o” thay vì kết quả của lệnh.

Ví dụ thực tế:

$ uname -o
GNU/LINUX
$ ./subshell.sh
GNU/LINUX
uname -o

Đọc dữ liệu người dùng nhập vào

Chúng ta có thể sử dụng lệnh read để nhận input từ người dùng. Cách này cho phép người dùng tương tác với Bash script và điều khiển luồng xử lý. Ví dụ dưới đây:

#!/bin/bash
 
echo -e "Hi, please type the word: \c "
read  word
echo "The word you entered is: $word"
echo -e "Can you please enter two words? "
read word1 word2
echo "Here is your input: \"$word1\" \"$word2\""
echo -e "How do you feel about bash scripting? "
# read command now stores a reply into the default build-in variable $REPLY
read
echo "You said $REPLY, I'm glad to hear that! "
echo -e "What are your favorite colours ? "
# -a makes read command to read into an array
read -a colours
echo "My favorite colours are also ${colours[0]}, ${colours[1]} and ${colours[2]}:-)"

Script Bash này sẽ lần lượt đặt câu hỏi cho người dùng và ghi nhận phản hồi vào biến và mảng, sau đó lặp lại những gì người dùng đã nhập:

$ ./read.sh
Hi, please type the word: Linuxconfig.org
The word you entered is: Linuxconfig.org
Can you please enter two words? 
Debian Linux
Here is your input: "Debian" "Linux"
How do you feel about bash scripting? 
good
You said good, I'm glad to hear that! 
What are your favorite colours ? 
blue green black
My favorite colours are also blue, green and black:-)

Lệnh trap trong Bash

Lệnh trap trong Bash cho phép script bắt được các tín hiệu hệ thống (signals), ví dụ như khi người dùng nhấn tổ hợp Ctrl + C, và thực hiện một hàm xử lý tương ứng.

Ví dụ bên dưới cho thấy cách trap hoạt động khi phát hiện tín hiệu INT (tín hiệu ngắt – interrupt):

#!/bin/bash
# bash trap command
trap bashtrap INT
# bash clear screen command
clear;
# bash trap function is executed when CTRL-C is pressed:
# bash prints message => Executing bash trap subrutine !
bashtrap()
{
    echo "CTRL+C Detected !...executing bash trap !"
}
# for loop from 1/10 to 10/10
for a in `seq 1 10`; do
    echo "$a/10 to Exit." 
    sleep 1;
done
echo "Exit Bash Trap Example!!!"

Kết quả khi thực thi và cố tình nhấn Ctrl + C:

$ ./trap.sh
1/10 to Exit.
2/10 to Exit.
^CCTRL+C Detected !...executing bash trap !
3/10 to Exit.
4/10 to Exit.
5/10 to Exit.
6/10 to Exit.
7/10 to Exit.
^CCTRL+C Detected !...executing bash trap !
8/10 to Exit.
9/10 to Exit.
10/10 to Exit.
Exit Bash Trap Example!!!

Bạn có thể thấy rằng mặc dù người dùng đã nhấn Ctrl+C, script không bị dừng lại mà vẫn tiếp tục thực thi nhờ xử lý qua trap.

Mảng trong Bash – Hướng dẫn Bash scripting

Bash hỗ trợ lưu trữ nhiều giá trị trong mảng (arrays). Dưới đây là hai ví dụ minh họa cách sử dụng mảng:

1. Khai báo mảng đơn giản trong Bash

Ví dụ sau khai báo một mảng với 4 phần tử:

#!/bin/bash
#Declare array with 4 elements
ARRAY=( 'Debian Linux' 'Redhat Linux' Ubuntu Linux )
# get number of elements in the array
ELEMENTS=${#ARRAY[@]}

# echo each element in array 
# for loop
for (( i=0;i<$ELEMENTS;i++)); do
    echo ${ARRAY[${i}]}
done

Kết quả khi chạy:

$ ./arrays.sh
Debian Linux
Redhat Linux
Ubuntu
Linux

2. Đọc nội dung file vào mảng trong Bash

Thay vì khai báo trực tiếp từng phần tử, chúng ta có thể đọc từ nội dung file để điền dữ liệu vào mảng:

#!/bin/bash
# Declare array
declare -a ARRAY
# Link filedescriptor 10 with stdin
exec 10<&0
# stdin replaced with a file supplied as a first argument
exec < $1
let count=0

while read LINE; do

    ARRAY[$count]=$LINE
    ((count++))
done

echo Number of elements: ${#ARRAY[@]}
# echo array's content
echo ${ARRAY[@]}
# restore stdin from filedescriptor 10
# and close filedescriptor 10
exec 0<&10 10<&-

Khi thực thi script, truyền vào một file văn bản chứa nội dung từng dòng. Mỗi dòng sẽ trở thành một phần tử của mảng.

$ cat bash.txt
Bash
Scripting
Tutorial
Guide
$ ./bash-script.sh bash.txt 
Number of elements: 4
Bash Scripting Tutorial Guide

Câu lệnh if / else / fi trong Bash

Dưới đây là một ví dụ đơn giản về câu lệnh if kiểm tra xem một thư mục có tồn tại hay không. Tùy vào kết quả kiểm tra, script sẽ thực hiện một trong hai hành động. Lưu ý quan trọng: phải có dấu cách giữa dấu [ và ]. Nếu không có khoảng trắng, câu lệnh sẽ không hoạt động!

#!/bin/bash
directory="./BashScripting"

# bash check if directory exists
if [ -d $directory ]; then
    echo "Directory exists"
else 
    echo "Directory does not exist"
fi

Kết quả khi chạy script:

$ ./bash_if_else.sh
Directory does not exist
$ mkdir BashScripting
$ ./bash_if_else.sh
Directory exists

if / else lồng nhau

Bạn hoàn toàn có thể đặt một câu lệnh if bên trong một if khác. Cách viết này gọi là nested if (câu lệnh lồng nhau). Tuy nhiên, script có thể trở nên phức tạp nếu lồng quá sâu nhiều tầng.

Ví dụ sau sẽ yêu cầu người dùng chọn từ một danh sách, và xử lý lựa chọn bằng if/else lồng nhau:

#!/bin/bash
 
# Declare variable choice and assign value 4
choice=4
# Print to stdout
 echo "1. Bash"
 echo "2. Scripting"
 echo "3. Tutorial"
 echo -n "Please choose a word [1,2 or 3]? "
# Loop while the variable choice is equal 4
# bash while loop
while [ $choice -eq 4 ]; do
 
# read user input
read choice
# bash nested if/else
if [ $choice -eq 1 ] ; then
 
        echo "You have chosen word: Bash"

else                   

        if [ $choice -eq 2 ] ; then
                 echo "You have chosen word: Scripting"
        else
         
                if [ $choice -eq 3 ] ; then
                        echo "You have chosen word: Tutorial"
                else
                        echo "Please make a choice between 1-3 !"
                        echo "1. Bash"
                        echo "2. Scripting"
                        echo "3. Tutorial"
                        echo -n "Please choose a word [1,2 or 3]? "
                        choice=4
                fi   
        fi
fi
done

Kết quả khi chạy script:

$ ./nested_if_else.sh 
1. Bash
2. Scripting
3. Tutorial
Please choose a word [1,2 or 3]? 5
Please make a choice between 1-3 !
1. Bash
2. Scripting
3. Tutorial
Please choose a word [1,2 or 3]? 2
You have chosen word: Scripting

So sánh trong Bash

Bash hỗ trợ so sánh các giá trị (số hoặc chuỗi) để kiểm tra chúng có bằng nhau hay không, hoặc lớn hơn/nhỏ hơn…

Các toán tử so sánh số học:

Toán tử Ý nghĩa
-lt nhỏ hơn <
-gt lớn hơn >
-le nhỏ hơn hoặc bằng <=
-ge lớn hơn hoặc bằng >=
-eq bằng ==
-ne không bằng !=

Ví dụ 1 – So sánh bằng nhau

#!/bin/bash
# declare integers
NUM1=2
NUM2=2
if [ $NUM1 -eq $NUM2 ]; then
    echo "Both values are equal"
else 
    echo "Values are NOT equal"
fi

Kết quả:

$ ./statement.sh
Both values are equal

Ví dụ 2 – So sánh không bằng

#!/bin/bash
# declare integers
NUM1=2
NUM2=1
if [ $NUM1 -eq $NUM2 ]; then
    echo "Both Values are equal"
else 
    echo "Values are NOT equal"
fi

Kết quả:

$ ./statement.sh
Values are NOT equal

Ví dụ 3 – Sử dụng elif để xác định số lớn hơn

#!/bin/bash
# declare integers
NUM1=2
NUM2=1
if   [ $NUM1 -eq $NUM2 ]; then
    echo "Both values are equal"
elif [ $NUM1 -gt $NUM2 ]; then
    echo "NUM1 is greater than NUM2"
else 
    echo "NUM2 is greater than NUM1"
fi

Kết quả:

$ ./statement.sh
NUM1 is greater than NUM2

So sánh chuỗi trong Bash

Toán tử Ý nghĩa
= bằng nhau (equal)
!= khác nhau (not equal)
< nhỏ hơn (less than)
> lớn hơn (greater than)
-n s1 chuỗi s1 không rỗng
-z s1 chuỗi s1 rỗng

Bước 1: Thử so sánh hai chuỗi để xem chúng có bằng nhau không:

#!/bin/bash
#Declare string S1
S1="Bash"
#Declare string S2
S2="Scripting"
if [ $S1 = $S2 ]; then
    echo "Both Strings are equal"
else 
    echo "Strings are NOT equal"
fi

Kết quả:

$ ./statement.sh
Strings are NOT equal

Bước 2: Thử lại với hai chuỗi giống hệt nhau:

#!/bin/bash
#Declare string S1
S1="Bash"
#Declare string S2
S2="Bash"
if [ $S1 = $S2 ]; then
    echo "Both Strings are equal"
else 
    echo "Strings are NOT equal"
fi

Kết quả:

$ ./statement.sh
Both Strings are equal

Kiểm tra file trong Bash

Trong Bash, bạn có thể kiểm tra nhiều thuộc tính khác nhau của file hoặc thư mục. Dưới đây là bảng tổng hợp:

Tùy chọn Mô tả
-b filename File đặc biệt dạng block
-c filename File đặc biệt dạng ký tự
-d directoryname Kiểm tra thư mục có tồn tại
-e filename Kiểm tra file có tồn tại
-f filename Kiểm tra file thông thường (không phải thư mục)
-G filename File tồn tại và thuộc group ID của user
-g filename File có bit set-group-ID
-k filename File có sticky bit
-L filename Là symbolic link
-O filename File tồn tại và thuộc quyền sở hữu của user
-r filename File có thể đọc được
-S filename File dạng socket
-s filename File có kích thước lớn hơn 0
-u filename File có set-user-ID bit
-w filename File có thể ghi được
-x filename File có thể thực thi được

Kiểm tra file có tồn tại hay không:

#!/bin/bash
file="./file"
if [ -e $file ]; then
    echo "File exists"
else 
    echo "File does not exist"
fi

Kết quả:

$ ./filetesting.sh
File does not exist
$ touch file
$ ./filetesting.sh
File exists

Tương tự, chúng ta có thể sử dụng vòng lặp while để kiểm tra xem một file chưa tồn tại. Script dưới đây sẽ tạm dừng (sleep) cho đến khi file được tạo ra (tức là tồn tại).

Lưu ý dấu ! trong Bash – đây là toán tử phủ định, dùng để phủ định điều kiện -e (kiểm tra file tồn tại).

#!/bin/bash
 
while [ ! -e myfile ]; do
# Sleep until file does exists/is created
sleep 1
done

Loops

Bash hỗ trợ nhiều loại vòng lặp như forwhile, và until. Dưới đây là một số ví dụ:

Bash for loop

Script sau sẽ liệt kê tất cả các file/thư mục trong thư mục /var/:

#!/bin/bash

# bash for loop
for f in $( ls /var/ ); do
    echo $f
done

Bạn cũng có thể chạy vòng lặp for ngay trong dòng lệnh:

$ for f in $( ls /var/ ); do echo $f; done

Kết quả:

$ ./for_loop.sh
backups
cache
crash
lib
local
lock
log
mail
metrics
opt
run
snap
spool
tmp

Bash while loop

Vòng lặp while này sẽ tiếp tục cho đến khi biến COUNT nhỏ hơn hoặc bằng 0:

#!/bin/bash
COUNT=6
# bash while loop
while [ $COUNT -gt 0 ]; do
    echo Value of count is: $COUNT
    let COUNT=COUNT-1
done

Kết quả:

$ ./while_loop.sh
Value of count is: 6
Value of count is: 5
Value of count is: 4
Value of count is: 3
Value of count is: 2
Value of count is: 1

Bash until loop

until loop tương tự như while, nhưng ngược điều kiện:

#!/bin/bash
COUNT=0
# bash until loop
until [ $COUNT -gt 5 ]; do
        echo Value of count is: $COUNT
        let COUNT=COUNT+1
done

Kết quả:

$ ./until_loop.sh
Value of count is: 0
Value of count is: 1
Value of count is: 2
Value of count is: 3
Value of count is: 4
Value of count is: 5

Điều khiển vòng lặp bằng input người dùng

Ví dụ này sử dụng vòng lặp while để tìm và thay thế khoảng trắng trong tên file bằng _:

#!/bin/bash
# This bash script will locate and replace spaces
# in the filenames
DIR="."
# Controlling a loop with bash read command by redirecting STDOUT as
# a STDIN to while loop
# find will not truncate filenames containing spaces
find $DIR -type f | while read file; do
# using POSIX class [:space:] to find space in the filename
if [[ "$file" = *[[:space:]]* ]]; then
# substitute space with "_" character and consequently rename the file
mv "$file" `echo $file | tr ' ' '_'`
fi;
# end of while loop
done

Hàm trong Bash

Ví dụ dưới đây cho thấy cách khai báo và gọi hàm trong Bash:

!/bin/bash
# BASH FUNCTIONS CAN BE DECLARED IN ANY ORDER
function function_B {
echo Function B.
}
function function_A {
echo $1
}
function function_D {
echo Function D.
}
function function_C {
echo $1
}
# FUNCTION CALLS
# Pass parameter to function A
function_A "Function A."
function_B
# Pass parameter to function C
function_C "Function C."
function_D

Kết quả khi chạy:

$ ./functions.sh
Function A.
Function B.
Function C.
Function D.

Bash Select – Tạo menu chọn trong Bash

Lệnh select trong Bash cho phép bạn tạo menu lựa chọn để người dùng dễ dàng tương tác.

#!/bin/bash
 
PS3='Choose one word: ' 

# bash select
select word in "linux" "bash" "scripting" "tutorial" 
do
  echo "The word you have selected is: $word"
# Break, otherwise endless loop
  break  
done

exit 0

Kết quả:

$ ./select.sh
1) linux
2) bash
3) scripting
4) tutorial
Choose one word: 2
The word you have selected is: bash

Câu lệnh điều kiện case trong Bash

Lệnh case giúp xử lý nhiều điều kiện một cách ngắn gọn và dễ đọc hơn so với if/else khi có nhiều lựa chọn:

#!/bin/bash
echo "What is your preferred programming / scripting language"
echo "1) bash"
echo "2) perl"
echo "3) phyton"
echo "4) c++"
echo "5) I do not know !"
read case;
#simple case bash structure
# note in this case $case is variable and does not have to
# be named case this is just an example
case $case in
    1) echo "You selected bash";;
    2) echo "You selected perl";;
    3) echo "You selected phyton";;
    4) echo "You selected c++";;
    5) exit
esac

Kết quả:

$ ./case.sh 
What is your preferred programming / scripting language
1) bash
2) perl
3) phyton
4) c++
5) I do not know !
3
You selected phyton

Dấu nháy và trích dẫn trong Bash

Dấu nháy và trích dẫn là một phần quan trọng trong Bash và lập trình Bash script. Dưới đây là những kiến thức cơ bản bạn cần nắm về cách sử dụng dấu nháy trong Bash.

Thoát ký tự đặc biệt

Trước khi tìm hiểu về dấu nháy và trích dẫn trong Bash, bạn cần hiểu khái niệm escape ký tự đặc biệt (meta characters). Việc escape sẽ vô hiệu hóa ý nghĩa đặc biệt của các ký tự đó, giúp Bash đọc chúng theo nghĩa chữ (literal) thay vì xử lý như lệnh hay biến.

Để escape ký tự đặc biệt, chúng ta sử dụng dấu gạch chéo ngược \. Ví dụ:

#!/bin/bash
 
#Declare bash string variable
BASH_VAR="Bash Script"

# echo variable BASH_VAR
echo $BASH_VAR

#when meta character such us "$" is escaped with "\" it will be read literally
echo \$BASH_VAR 

# backslash has also special meaning and it can be suppressed with yet another "\"
echo "\\"

Kết quả:

$ ./escape_meta.sh
Bash Script
$BASH_VAR
\

Dấu nháy đơn ' '

Khi đặt nội dung trong dấu nháy đơn, mọi ký tự đặc biệt bên trong sẽ bị vô hiệu hóa, kể cả dấu $.

#!/bin/bash
 
# Declare bash string variable
BASH_VAR="Bash Script"
 
# echo variable BASH_VAR
echo $BASH_VAR
 
# meta characters special meaning in bash is suppressed when  using single quotes 
echo '$BASH_VAR  "$BASH_VAR"'

Kết quả:

$ ./single_quotes.sh
Bash Script
$BASH_VAR "$BASH_VAR"

Dấu nháy kép " "

Trong Bash, dấu nháy kép (" ") sẽ vô hiệu hóa ý nghĩa đặc biệt của hầu hết các ký tự đặc biệt, ngoại trừ:

  • $ (biến),
  • \ (escape),
  • và ` (lệnh nội dòng – command substitution).

Tất cả các ký tự đặc biệt khác sẽ được Bash hiểu theo nghĩa chữ (literal).

Ngoài ra, bạn có thể đặt dấu nháy đơn (') bên trong dấu nháy kép mà không gặp lỗi. Nếu bạn muốn sử dụng dấu nháy kép bên trong một cặp dấu nháy kép, thì cần escape bằng ký tự \.

Ví dụ:

#!/bin/bash
 
#Declare bash string variable
BASH_VAR="Bash Script"

# echo variable BASH_VAR
echo $BASH_VAR

# meta characters and its special meaning in bash is 
# suppressed when using double quotes except "$", "\" and "`"

echo "It's $BASH_VAR  and \"$BASH_VAR\" using backticks: `date`"

Kết quả:

$ ./double_quotes.sh
Bash Script
It's Bash Script and "Bash Script" using backticks: Thu 10 Feb 2022 10:24:15 PM EST

Trích dẫn kiểu ANSI-C ($'...')

Ngoài các kiểu trích dẫn thông thường, Bash còn hỗ trợ một kiểu trích dẫn khác gọi là trích dẫn theo chuẩn ANSI-C.

Với kiểu trích dẫn này, các ký tự được escape bằng dấu \ sẽ mang ý nghĩa đặc biệt theo quy chuẩn của ANSI-C.

Dưới đây là một số ký tự escape phổ biến:

  • \a – chuông cảnh báo (alert/bell)
  • \b – backspace
  • \e – ký tự escape
  • \f – form feed
  • \n – xuống dòng (newline)
  • \r – quay về đầu dòng (carriage return)
  • \t – tab ngang
  • \v – tab dọc
  • \\ – ký tự backslash
  • \' – dấu nháy đơn
  • \nnn – ký tự có giá trị bát phân (octal)
  • \xnn – ký tự có giá trị thập lục phân (hex)

Cú pháp trích dẫn kiểu ANSI-C trong Bash là: $' '

#!/bin/bash
 
# as a example we have used \n as a new line, \x40 is hex value for @
# and \56 is octal value for .
echo $'web: www.linuxconfig.org\nemail: web\x40linuxconfig\56org'

Kết quả:

$ ./bash_ansi-c.sh
web: www.linuxconfig.org
email: web@linuxconfig.org

Toán học trong Bash

Bash có thể được sử dụng để thực hiện các phép tính toán. Hãy cùng xem một vài ví dụ để hiểu cách thực hiện điều đó trong thực tế.

Cộng hai số – ví dụ đơn giản:

#!/bin/bash
 
let RESULT1=$1+$2
echo $1+$2=$RESULT1 ' -> # let RESULT1=$1+$2'
declare -i RESULT2
RESULT2=$1+$2
echo $1+$2=$RESULT2 ' -> # declare -i RESULT2; RESULT2=$1+$2'
echo $1+$2=$(($1 + $2)) ' -> # $(($1 + $2))'

Kết quả:

$ ./bash_addition_calc.sh 88 12
88+12=100  -> # let RESULT1=$1+$2
88+12=100  -> # declare -i RESULT2; RESULT2=$1+$2
88+12=100  -> # $(($1 + $2))

Phép toán số học cơ bản trong Bash

Hãy cùng tìm hiểu cách thực hiện một số phép toán cơ bản trong Bash như cộng, trừ, nhân, chia, v.v.

#!/bin/bash
 
echo '### let ###'
# bash addition
let ADDITION=3+5
echo "3 + 5 =" $ADDITION

# bash subtraction
let SUBTRACTION=7-8
echo "7 - 8 =" $SUBTRACTION 

# bash multiplication
let MULTIPLICATION=5*8
echo "5 * 8 =" $MULTIPLICATION

# bash division
let DIVISION=4/2
echo "4 / 2 =" $DIVISION

# bash modulus
let MODULUS=9%4
echo "9 % 4 =" $MODULUS

# bash power of two
let POWEROFTWO=2**2
echo "2 ^ 2 =" $POWEROFTWO


echo '### Bash Arithmetic Expansion ###'
# There are two formats for arithmetic expansion: $[ expression ] 
# and $(( expression #)) its your choice which you use

echo 4 + 5 = $((4 + 5))
echo 7 - 7 = $[ 7 - 7 ]
echo 4 x 6 = $((3 * 2))
echo 6 / 3 = $((6 / 3))
echo 8 % 7 = $((8 % 7))
echo 2 ^ 8 = $[ 2 ** 8 ]


echo '### Declare ###'

echo -e "Please enter two numbers \c"
# read user input
read num1 num2
declare -i result
result=$num1+$num2
echo "Result is:$result "

# bash convert binary number 10001
result=2#10001
echo $result

# bash convert octal number 16
result=8#16
echo $result

# bash convert hex number 0xE6A
result=16#E6A
echo $result

Kết quả:

$ ./arithmetic_operations.sh 
### let ###
3 + 5 = 8
7 - 8 = -1
5 * 8 = 40
4 / 2 = 2
9 % 4 = 1
2 ^ 2 = 4
### Bash Arithmetic Expansion ###
4 + 5 = 9
7 - 7 = 0
4 x 6 = 6
6 / 3 = 2
8 % 7 = 1
2 ^ 8 = 256
### Declare ###
Please enter two numbers 23 45
Result is:68 
17
14
3690

Làm tròn số thực trong Bash

#!/bin/bash
# get floating point number
floating_point_number=3.3446
echo $floating_point_number
# round floating point number with bash
for bash_rounded_number in $(printf %.0f $floating_point_number); do
echo "Rounded number with bash:" $bash_rounded_number
done

Kết quả:

$ ./round.sh
3.3446
Rounded number with bash: 3

Tính toán số thực với bc

#!/bin/bash
# Simple linux bash calculator 
echo "Enter input:" 
read userinput
echo "Result with 2 digits after decimal point:"
echo "scale=2; ${userinput}" | bc 
echo "Result with 10 digits after decimal point:"
echo "scale=10; ${userinput}" | bc 
echo "Result as rounded integer:"
echo $userinput | bc

Kết quả:

$ ./simple_bash_calc.sh
Enter input:
10/3.4
Result with 2 digits after decimal point:
2.94
Result with 10 digits after decimal point:
2.9411764705
Result as rounded integer:
2

Chuyển hướng trong Bash

Trong các ví dụ sau đây, chúng ta sẽ tìm hiểu cách chuyển hướng (redirect) lỗi chuẩn (standard error – STDERR) và đầu ra chuẩn (standard output – STDOUT) trong Bash.

Chuyển hướng STDOUT sang STDERR

#!/bin/bash
 
echo "Redirect this STDOUT to STDERR" 1>&2

Để chứng minh rằng STDOUT đã được chuyển hướng sang STDERR, chúng ta có thể chuyển hướng đầu ra của script vào một file như sau:

$ ./redirecting.sh
Redirect this STDOUT to STDERR
$ ./redirecting.sh > STDOUT.txt
$ cat STDOUT.txt
$ 
$ ./redirecting.sh 2> STDERR.txt
$ cat STDERR.txt
Redirect this STDOUT to STDERR

Chuyển hướng STDERR sang STDOUT

#!/bin/bash
 
cat $1 2>&1

Để chứng minh rằng STDERR đã được chuyển hướng sang STDOUT, chúng ta có thể chuyển hướng đầu ra của script vào một file như sau:

$ ./redirecting.sh /etc/shadow
cat: /etc/shadow: Permission denied
$ ./redirecting.sh /etc/shadow > STDOUT.txt
$ cat STDOUT.txt
cat: /etc/shadow: Permission denied
$ ./redirecting.sh /etc/shadow 2> STDERR.txt
cat: /etc/shadow: Permission denied
$ cat STDERR.txt
$

Chuyển hướng STDOUT ra màn hình

Cách đơn giản nhất để chuyển hướng đầu ra chuẩn (stdout) là chỉ cần sử dụng bất kỳ lệnh nào, vì mặc định stdout sẽ tự động hiển thị ra màn hình.

Trước tiên, hãy tạo một file tên là file1:

$ touch file1
$ ls file1 
file1

Như bạn có thể thấy từ ví dụ trên, khi thực thi lệnh ls, nó tạo ra STDOUT và theo mặc định, được hiển thị lên màn hình.

Chuyển hướng stdout sang file

Để thay đổi hành vi mặc định của STDOUT, chúng ta có thể sử dụng ký hiệu > để chuyển hướng đầu ra này vào một file như sau:

$ ls file1 > STDOUT
$ cat STDOUT 
file1

Chuyển STDERR sang file

Mặc định, STDERR (lỗi chuẩn) sẽ được hiển thị trên màn hình:

$ ls
file1  STDOUT
$ ls file2
ls: cannot access file2: No such file or directory

Trong ví dụ dưới đây, chúng ta sẽ chuyển hướng STDERR vào một file, còn STDOUT vẫn hiển thị trên màn hình như bình thường.

Lưu ý rằng: kết quả của file1 (STDOUT) được in ra màn hình, trong khi lỗi liên quan đến file2 (STDERR) được ghi vào file có tên là STDERR:

$ ls
file1  STDOUT
$ ls file1 file2 2> STDERR
file1
$ cat STDERR 
ls: cannot access file2: No such file or directory

Chuyển hướng STDOUT sang STDERR

Bạn cũng có thể chuyển hướng cả STDOUT (đầu ra chuẩn) và STDERR (lỗi chuẩn) vào cùng một file. Trong ví dụ dưới đây, chúng ta sẽ chuyển STDOUT về cùng bộ mô tả (descriptor) với STDERR, do đó cả hai sẽ được ghi vào file có tên là STDERR_STDOUT:

$ ls
file1  STDERR  STDOUT
$ ls file1 file2 2> STDERR_STDOUT 1>&2
$ cat STDERR_STDOUT
ls: cannot access file2: No such file or directory
file1

File STDERR_STDOUT giờ đây đã chứa cả STDOUT và STDERR.

Chuyển hướng STDERR sang STDOUT

Bạn cũng có thể đảo ngược ví dụ trên, bằng cách chuyển STDERR về cùng descriptor với STDOUT:

$ ls
file1  STDERR  STDOUT
$ ls file1 file2 > STDERR_STDOUT 2>&1
$ cat STDERR_STDOUT 
ls: cannot access file2: No such file or directory
file1

Chuyển cả STDOUT và STDERR vào một file

Hai ví dụ trước đều cho thấy cách chuyển hướng cả STDOUT và STDERR vào cùng một file. Dưới đây là một cách viết khác để đạt cùng mục tiêu đó:

$ ls
file1  STDERR  STDOUT
$ ls file1 file2 &> STDERR_STDOUT
$ cat STDERR_STDOUT 
ls: cannot access file2: No such file or directory
file1

Hoặc:

ls file1 file2 >& STDERR_STDOUT
$ cat STDERR_STDOUT 
ls: cannot access file2: No such file or directory
file1

Tổng kết

Trong hướng dẫn Bash scripting này, bạn đã được làm quen với những nền tảng quan trọng nhất để bắt đầu hành trình làm chủ dòng lệnh – từ vòng lặp, toán học, so sánh giá trị, xử lý lỗi, đến trích dẫn và chuyển hướng dữ liệu. Mỗi phần đều mở ra một góc nhìn mới về cách mà Bash hoạt động và cách bạn có thể tận dụng nó để tự động hóa quy trình, tối ưu hiệu suất làm việc, hoặc đơn giản là trở thành người dùng Linux thành thạo hơn.

Đây chỉ là bước khởi đầu. Khi bạn tiếp tục luyện tập, thử nghiệm và sáng tạo với các ví dụ trong bài viết – biến chúng thành công cụ phục vụ cho công việc thực tế của bạn – thì kỹ năng Bash scripting của bạn sẽ tiến bộ một cách tự nhiên và vững chắc.

Hãy tiếp tục viết script, tự động hóa những việc lặp đi lặp lại, và biến terminal thành môi trường làm việc mạnh mẽ nhất của bạn!

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *