This programming guide is only available in Vietnamese. Switch to Vietnamese to read the full article.

Sức mạnh thực sự của Bash nằm ở khả năng xử lý text. Với hệ thống redirection, pipeline và bộ ba công cụ grep, sed, awk, bạn có thể biến đổi, lọc và phân tích dữ liệu text một cách cực kỳ hiệu quả ngay từ dòng lệnh. Bài học này sẽ hướng dẫn bạn từng công cụ một cách chi tiết.

1. Redirection: stdin, stdout, stderr

Mỗi tiến trình trong Linux/Unix có 3 file descriptor mặc định:

  • 0stdin (standard input): dữ liệu đầu vào
  • 1stdout (standard output): kết quả bình thường
  • 2stderr (standard error): thông báo lỗi

Redirection cho phép bạn chuyển hướng các luồng dữ liệu này sang file hoặc sang nhau.

redirection_basics.sh
#!/bin/bash

# Ghi đè file (overwrite) với >
echo "Dòng đầu tiên" > output.txt

# Nối thêm vào file (append) với >>
echo "Dòng thứ hai" >> output.txt

# Đọc input từ file với <
wc -l < output.txt    # Đếm số dòng trong file

# Redirect stderr (fd 2) sang file
ls /thu_muc_khong_ton_tai 2> error.log

# Redirect cả stdout và stderr sang cùng file
ls /home /xxx 2>&1 > all_output.log
# Hoặc cú pháp ngắn gọn hơn (Bash 4+):
ls /home /xxx &> all_output.log

# Bỏ qua output hoàn toàn với /dev/null
ping -c 1 google.com > /dev/null 2>&1
echo "Ping xong, exit code: $?"

# Redirect stdout sang file, stderr sang file khác
command_nao_do > success.log 2> error.log

Here Document và Here String

Here Document (<<EOF) cho phép bạn truyền nhiều dòng text vào stdin của một lệnh. Here String (<<<) truyền một chuỗi đơn.

heredoc_herestring.sh
#!/bin/bash

# Here Document — tạo file config nhanh
cat <<EOF > config.ini
[database]
host=localhost
port=5432
name=myapp_db
EOF

# Here Document với tab indentation (dùng <<- để bỏ tab đầu dòng)
if true; then
    cat <<-EOF
	Dòng này có tab ở đầu sẽ bị loại bỏ
	Tiện lợi khi viết trong block indent
	EOF
fi

# Here String — truyền chuỗi vào stdin
grep "hello" <<< "hello world, xin chào"

# Đếm từ trong một chuỗi
wc -w <<< "Bash là ngôn ngữ shell scripting"

# Tách chuỗi bằng read với here string
IFS=',' read -r name age city <<< "Quang,25,HCM"
echo "Tên: $name, Tuổi: $age, TP: $city"

2. Pipeline |

Pipeline (|) là cốt lõi của triết lý Unix: "Mỗi chương trình làm một việc tốt, và kết nối chúng lại với nhau." Pipe lấy stdout của lệnh bên trái làm stdin của lệnh bên phải.

pipeline.sh
#!/bin/bash

# Đếm số lỗi 404 trong access log
cat access.log | grep "404" | wc -l

# Top 10 IP truy cập nhiều nhất
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

# Liệt kê tất cả user đang chạy process
ps aux | awk '{print $1}' | sort -u | tail -n +2

# Tìm 5 file lớn nhất trong thư mục hiện tại
du -sh * 2>/dev/null | sort -rh | head -5

# Pipeline kết hợp nhiều bước xử lý
echo "hello world foo bar baz" | tr ' ' '\n' | sort | uniq | wc -l

Lệnh tee — ghi ra file đồng thời hiển thị

tee cho phép bạn "rẽ nhánh" dữ liệu: vừa ghi vào file, vừa tiếp tục pipeline.

tee_example.sh
#!/bin/bash

# Vừa hiển thị ra màn hình, vừa lưu vào file
ls -la | tee listing.txt

# Append thay vì overwrite
date | tee -a log.txt

# Ghi vào nhiều file cùng lúc
echo "Thông báo quan trọng" | tee file1.txt file2.txt file3.txt

# Kết hợp trong pipeline dài
cat data.csv | tee raw_backup.csv | grep "active" | tee filtered.csv | wc -l

3. grep — Tìm kiếm Pattern

grep (Global Regular Expression Print) là công cụ tìm kiếm text mạnh nhất trong Unix. Nó tìm các dòng khớp với pattern và in ra kết quả.

grep_basics.sh
#!/bin/bash

# Tìm kiếm cơ bản
grep "error" /var/log/syslog

# -i: không phân biệt hoa thường
grep -i "warning" app.log

# -n: hiển thị số dòng
grep -n "TODO" *.py

# -r: tìm đệ quy trong thư mục
grep -r "import React" src/

# -c: đếm số dòng khớp
grep -c "404" access.log

# -v: đảo ngược — hiển thị dòng KHÔNG khớp
grep -v "^#" config.ini    # Bỏ dòng comment

# -l: chỉ hiển thị tên file chứa pattern
grep -rl "deprecated" --include="*.js" src/

# -w: khớp nguyên từ (word boundary)
grep -w "log" script.sh    # Khớp "log" nhưng không khớp "logging"

# -A, -B, -C: hiển thị dòng xung quanh kết quả
grep -A 3 "ERROR" app.log   # 3 dòng sau
grep -B 2 "ERROR" app.log   # 2 dòng trước
grep -C 2 "ERROR" app.log   # 2 dòng trước và sau

Extended Regex với grep -E

grep_regex.sh
#!/bin/bash

# -E: Extended Regular Expression (hoặc dùng egrep)
# Tìm email
grep -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' contacts.txt

# Tìm IP address
grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log

# Tìm nhiều pattern với |
grep -E "error|warning|critical" app.log

# Tìm dòng bắt đầu bằng số
grep -E "^[0-9]" data.txt

# Tìm dòng trống
grep -E "^$" file.txt

# Tìm URL
grep -Eo 'https?://[^ ]+' page.html

# -o: chỉ in phần khớp (không in cả dòng)
echo "Giá: 150000 VND" | grep -oE '[0-9]+'
# Output: 150000

# -P: Perl-compatible regex (hỗ trợ lookahead/lookbehind)
echo "version=3.14.1" | grep -oP '(?<=version=)\S+'
# Output: 3.14.1

4. sed — Stream Editor

sed (Stream Editor) là công cụ chỉnh sửa text theo luồng. Nó đọc từng dòng, áp dụng lệnh biến đổi, và in ra kết quả — không thay đổi file gốc (trừ khi dùng -i).

sed_basics.sh
#!/bin/bash

# Thay thế cơ bản: s/old/new/
echo "Hello World" | sed 's/World/Vietnam/'
# Output: Hello Vietnam

# g flag: thay thế TẤT CẢ occurrences trên dòng
echo "aaa bbb aaa" | sed 's/aaa/xxx/g'
# Output: xxx bbb xxx

# Không có g chỉ thay thế lần đầu tiên
echo "aaa bbb aaa" | sed 's/aaa/xxx/'
# Output: xxx bbb aaa

# -i: chỉnh sửa file trực tiếp (in-place)
sed -i 's/old_value/new_value/g' config.txt

# Trên macOS cần backup extension:
sed -i '.bak' 's/old/new/g' config.txt

# Xóa dòng chứa pattern
sed '/^#/d' config.ini         # Xóa dòng comment
sed '/^$/d' file.txt           # Xóa dòng trống

# Xóa theo số dòng
sed '5d' file.txt              # Xóa dòng 5
sed '3,7d' file.txt            # Xóa dòng 3 đến 7

# In dòng cụ thể (kết hợp -n)
sed -n '10p' file.txt          # In dòng 10
sed -n '5,10p' file.txt        # In dòng 5 đến 10
sed -n '/pattern/p' file.txt   # In dòng khớp pattern

sed nâng cao

sed_advanced.sh
#!/bin/bash

# Address ranges — chỉ thay thế trong phạm vi dòng
sed '2,5s/foo/bar/g' file.txt           # Chỉ thay ở dòng 2-5
sed '/START/,/END/s/old/new/g' file.txt # Từ dòng chứa START đến END

# Capture groups với \( \) và back-reference \1
echo "2026-06-24" | sed 's/\([0-9]*\)-\([0-9]*\)-\([0-9]*\)/\3\/\2\/\1/'
# Output: 24/06/2026

# Chèn dòng trước/sau
sed '3i\Dòng mới chèn trước dòng 3' file.txt
sed '3a\Dòng mới chèn sau dòng 3' file.txt

# Nhiều lệnh sed cùng lúc
sed -e 's/foo/bar/g' -e 's/baz/qux/g' -e '/^$/d' file.txt

# Thay đổi delimiter (hữu ích khi pattern chứa /)
sed 's|/usr/local/bin|/opt/bin|g' paths.txt
sed 's#http://#https://#g' urls.txt

# Thêm prefix/suffix cho mỗi dòng
sed 's/^/PREFIX: /' file.txt    # Thêm prefix
sed 's/$/ :SUFFIX/' file.txt    # Thêm suffix

5. awk — Field Processing

awk là ngôn ngữ xử lý text mạnh mẽ nhất trong bộ ba. Nó tự động chia mỗi dòng thành các field ($1, $2, ...) và cho phép bạn thực hiện tính toán, điều kiện, vòng lặp.

awk_basics.sh
#!/bin/bash

# In field cụ thể (mặc định tách bằng khoảng trắng)
echo "Alice 90 85 92" | awk '{print $1, $3}'
# Output: Alice 85

# $0 = toàn bộ dòng, NR = số dòng, NF = số field
echo -e "a b c\nd e f g" | awk '{print "Dòng", NR, "có", NF, "field:", $0}'

# -F: thay đổi field separator
echo "name,age,city" | awk -F',' '{print $2}'
# Output: age

# Xử lý file CSV
awk -F',' '{print $1, $3}' employees.csv

# Pattern matching — chỉ xử lý dòng khớp
awk '/error/ {print NR, $0}' app.log

# So sánh field
awk -F',' '$3 > 50000 {print $1, $3}' salaries.csv

# BEGIN và END blocks
awk 'BEGIN {print "=== BÁO CÁO ==="}
     {print NR". "$0}
     END {print "Tổng:", NR, "dòng"}' data.txt

awk nâng cao — Tính toán và thống kê

awk_advanced.sh
#!/bin/bash

# Tính tổng cột
awk -F',' '{sum += $2} END {print "Tổng:", sum}' sales.csv

# Tính trung bình
awk -F',' '{sum += $2; count++} END {print "TB:", sum/count}' scores.csv

# Tìm giá trị lớn nhất
awk -F',' 'BEGIN {max=0} $2 > max {max=$2; name=$1} END {print name, max}' data.csv

# Format output với printf
awk -F',' '{printf "%-20s %10.2f VND\n", $1, $2}' prices.csv

# Associative array — đếm theo nhóm
awk -F',' '{count[$1]++} END {for (k in count) print k, count[k]}' log.csv

# Điều kiện if-else
awk -F',' '{
    if ($3 >= 90) grade="A"
    else if ($3 >= 80) grade="B"
    else if ($3 >= 70) grade="C"
    else grade="F"
    print $1, grade
}' students.csv

Các công cụ bổ sung: cut, sort, uniq, tr, wc, xargs

text_tools.sh
#!/bin/bash

# cut — cắt field/ký tự từ mỗi dòng
cut -d',' -f1,3 data.csv       # Lấy field 1 và 3 (delimiter = ,)
cut -c1-10 file.txt             # Lấy 10 ký tự đầu mỗi dòng

# sort — sắp xếp
sort file.txt                    # Sắp xếp theo alphabet
sort -n numbers.txt              # Sắp xếp theo số
sort -r file.txt                 # Đảo ngược
sort -t',' -k2 -n data.csv      # Sắp xếp theo cột 2 (số)
sort -u file.txt                 # Sắp xếp + loại bỏ trùng

# uniq — loại bỏ dòng trùng liên tiếp (thường dùng sau sort)
sort names.txt | uniq            # Loại trùng
sort names.txt | uniq -c         # Đếm số lần xuất hiện
sort names.txt | uniq -d         # Chỉ hiện dòng trùng

# tr — translate/delete ký tự
echo "hello" | tr 'a-z' 'A-Z'              # Chuyển thành HOA
echo "hello   world" | tr -s ' '           # Squeeze khoảng trắng
echo "abc123def" | tr -d '0-9'             # Xóa số → abcdef
cat file.txt | tr '\n' ' '                 # Nối tất cả dòng thành 1

# wc — word count
wc -l file.txt     # Đếm dòng
wc -w file.txt     # Đếm từ
wc -c file.txt     # Đếm byte

# xargs — chuyển stdin thành arguments
find . -name "*.tmp" | xargs rm           # Xóa file .tmp
grep -rl "old_func" src/ | xargs sed -i 's/old_func/new_func/g'
echo "1 2 3 4 5" | xargs -n 2 echo       # Chia thành nhóm 2

6. Câu hỏi trắc nghiệm ôn tập

Trắc nghiệm: Pipeline & grep

Lệnh nào đếm số dòng chứa từ "error" (không phân biệt hoa thường) trong file app.log?

Trắc nghiệm: sed & awk

Trong awk, biến NF đại diện cho gì?

Tải file code thực hành minh họa bài học

File script tổng hợp tất cả ví dụ về redirection, pipeline, grep, sed, awk và các công cụ xử lý text khác.

Tải về text_processing.sh

Related Articles

Bài viết liên quan trong series

Lesson 6: Process & Signals in Bash: Process Management & trap Bài 6: Process & Signals trong Bash: Quản Lý Tiến Trình & trap Lesson 4: Functions & Scripts: Writing Your First Professional Script Bài 4: Functions & Scripts — Viết Script Chuyên Nghiệp Đầu Tiên Back to Bash Series Overview Quay lại Lộ trình Bash Series