zalo-icon
facebook-icon
phone-icon
Linux Command Line thực chiến: Quản lý file, thư mục và dữ liệu | CLI Guide
Hướng dẫn Linux Command Line thực chiến để quản lý file, tìm kiếm, lọc dữ liệu, archive, checksum và xây pipeline shell an toàn.

Command line mạnh không phải vì nhiều lệnh, mà vì có thể kết hợp các công cụ nhỏ thành quy trình rõ ràng, kiểm tra được và tự động hóa được.

Bài viết dùng bộ file bán hàng theo ngày để thực hành điều hướng, tìm kiếm, lọc, sắp xếp, nén, checksum và pipeline shell. Mỗi lệnh được đặt trong một câu hỏi dữ liệu cụ thể.

Sản phẩm người đọc sẽ hoàn thành

Sau bài viết, người học hoàn thành một CLI data audit tạo manifest, báo cáo chất lượng file, archive và checksum. Project được tổ chức thành các thư mục src, config, logs, tests và evidence. README ghi rõ phiên bản Ubuntu, kiến trúc máy, user thực thi, cách chạy, cách kiểm thử và cách khôi phục.

Pipe tee archive và checksum trong Linux
Bộ công cụ xử lý dữ liệu văn bản Linux
Tìm file và lọc dữ liệu với find grep
Thao tác file thư mục an toàn
Điều hướng và đường dẫn trong Linux

Bộ dữ liệu hoặc workload thực hành là 30 file CSV bán hàng, log ingest và thư mục đầu ra theo ngày. Trước khi bắt đầu, tạo snapshot hoặc bản sao dữ liệu, xác nhận còn đủ dung lượng và mở một terminal thứ hai để cứu hộ nếu phiên làm việc chính gặp lỗi. Những nguyên tắc này giúp bài tập gần với cách vận hành Linux trong doanh nghiệp.

Môi trường và quy ước an toàn

mkdir -p ~/linux-lab/{src,config,logs,tests,evidence}
cd ~/linux-lab
printf "started_at=%s\n" "$(date -Is)" | tee evidence/session.txt
uname -a | tee -a evidence/session.txt
cat /etc/os-release | tee -a evidence/session.txt

Bài thực hành dùng Ubuntu Server LTS trên máy ảo tối thiểu 2 vCPU, 4 GB RAM và 20 GB ổ đĩa. Không chạy trực tiếp trên máy production. Những lệnh có khả năng xóa dữ liệu, sửa quyền hoặc khởi động lại service phải được thử trên thư mục lab trước, đồng thời ghi lại trạng thái trước và sau.

1. Điều hướng và nhận diện ngữ cảnh

Lệnh đúng ở thư mục sai vẫn gây hỏng dữ liệu. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. In working directory, liệt kê có kiểm soát và dùng realpath. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/1. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

pwd
ls -lah
realpath .
printf "%q\n" "$PWD"

Kết quả cần thấy. Biết chính xác vị trí, user và các file ẩn trước thao tác. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Script cd tới BASE_DIR đã kiểm tra hoặc dùng đường dẫn tuyệt đối. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

2. Tạo cấu trúc dữ liệu an toàn

Cấu trúc thư mục phản ánh vòng đời raw, staging và output. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Tạo cây thư mục, dùng install thay mkdir khi cần mode rõ. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/2. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

install -d -m 0750 data/{raw,staging,processed,rejected}
find data -maxdepth 2 -type d -print

Kết quả cần thấy. Bốn vùng tồn tại với quyền mong đợi. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Không trộn file nguồn với output để retry không nhân bản dữ liệu. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

3. Copy move và rename

Ghi đè không cảnh báo dễ làm mất bản đúng. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Dùng cp no-clobber, mv có staging và kiểm tra trước sau. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/3. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

cp -n sample.csv data/raw/
cp -a data/raw data/raw_backup
mv data/raw/sample.csv data/staging/

Kết quả cần thấy. Metadata được giữ ở backup và file chỉ xuất hiện tại đích cuối. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Di chuyển xuyên filesystem không nguyên tử; cần thiết kế temp path cùng mount. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

4. Tìm file bằng find

Tên file và thời gian sửa giúp khoanh vùng batch lỗi. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Tìm CSV theo ngày, kích thước và tuổi; dùng print0 cho tên đặc biệt. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/4. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

find data/raw -type f -name "*.csv" -size +0 -mtime -7 -print0 | xargs -0 -r ls -lh

Kết quả cần thấy. Chỉ file thỏa điều kiện được xử lý và tên có khoảng trắng không vỡ. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Không dùng find -delete trước khi in và review danh sách mục tiêu. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

5. Đọc dữ liệu với head tail wc

Sample nhanh giúp phát hiện delimiter và header. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Xem đầu cuối, đếm dòng và đo byte mà không mở editor. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/5. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

head -3 data/raw/sales.csv
tail -3 data/raw/sales.csv
wc -l -c data/raw/sales.csv

Kết quả cần thấy. Header và sample hợp lệ, số dòng lớn hơn một. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. File lớn được kiểm tra streaming; không cat toàn bộ vào terminal. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

6. Lọc với grep

Log và file văn bản có thể được sàng lọc theo pattern. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Dùng grep cố định cho mã lỗi và regex cho cấu trúc dòng. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/6. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

grep -nF "ERROR" logs/ingest.log | tail -20
grep -Ev "^[0-9]+,[0-9-]+,[0-9.]+$" data/raw/sales.csv | head

Kết quả cần thấy. Tìm đúng lỗi và các dòng không khớp schema đơn giản. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Regex validation chỉ là lớp đầu, không thay parser CSV chuẩn. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

7. Biến đổi với cut sort uniq

Công cụ text phù hợp dữ liệu đơn giản có delimiter ổn định. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Trích region, sắp xếp và đếm tần suất. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/7. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

tail -n +2 data/raw/sales.csv | cut -d, -f3 | sort | uniq -c | sort -nr

Kết quả cần thấy. Danh sách vùng được đếm và xếp giảm dần. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Không dùng cut cho CSV có dấu phẩy trong quoted field; chuyển sang Python hoặc Miller. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

8. Kết hợp awk

awk xử lý tổng hợp nhẹ mà không tải toàn bộ vào RAM. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Tính tổng revenue theo region và kiểm tra cột số. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/8. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

awk -F, "NR>1 {sum[$3]+=$4} END {for(k in sum) print k,sum[k]}" data/raw/sales.csv | sort

Kết quả cần thấy. Mỗi region có một tổng và tổng chung đối soát được. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Đặt locale và xử lý header, missing, quote rõ ràng trước production. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

9. Nén archive và checksum

Archive cần chứng minh không hỏng trong truyền hoặc lưu trữ. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Tạo tar gzip, checksum rồi test nội dung. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/9. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

tar -czf sales-archive.tgz data/raw
sha256sum sales-archive.tgz | tee evidence/archive.sha256
tar -tzf sales-archive.tgz | head

Kết quả cần thấy. Archive đọc được và checksum xác minh thành công. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Mã hóa nếu dữ liệu nhạy cảm và lưu checksum tách khỏi file. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

10. Pipe redirection và tee

Pipeline nối công cụ nhưng có thể che lỗi upstream. Trong bối cảnh analyst xử lý hàng nghìn file CSV trên server mà không có giao diện đồ họa, bước này không chỉ nhằm ghi nhớ câu lệnh mà còn giúp hiểu trạng thái của hệ thống trước khi thay đổi. Mọi thao tác đều cần có đầu vào, kết quả mong đợi và cách quay lại trạng thái an toàn nếu xảy ra lỗi.

Thực hành. Bật pipefail, tách stdout stderr và giữ bản log qua tee. Chạy lệnh bằng tài khoản không có quyền root, quan sát mã thoát với echo $?, rồi lưu kết quả vào thư mục evidence/10. Không sao chép lệnh có sudo trước khi hiểu chính xác file, service hoặc quyền sẽ bị thay đổi.

set -o pipefail
find data/raw -name "*.csv" -print | sort | tee evidence/manifest.txt
wc -l evidence/manifest.txt

Kết quả cần thấy. Manifest có thứ tự ổn định và pipeline trả lỗi nếu thành phần lỗi. Nếu đầu ra khác dự kiến, cần đọc thông báo lỗi từ trái sang phải, xác nhận đường dẫn tuyệt đối, quyền truy cập, biến môi trường và trạng thái tiến trình. Đây là thói quen quan trọng khi làm việc với server thật, nơi một lệnh chạy thành công chưa chắc tạo ra kết quả nghiệp vụ đúng.

Góc nhìn production. Log cần timestamp, run ID và retention; không redirect chồng lên file nguồn. Với Linux command line, tiêu chí hoàn thành gồm khả năng chạy lại, log đủ để điều tra, quyền tối thiểu, cấu hình có phiên bản và bằng chứng kiểm thử. Một thao tác thủ công chỉ được chấp nhận khi có lý do, người phê duyệt và kế hoạch tự động hóa sau đó.

Kiểm thử cuối bài

Kiểm thử gồm bốn lớp. Lớp chức năng xác nhận manifest đủ file, tổng số dòng đúng, file lỗi được tách riêng và archive khớp checksum. Lớp dữ liệu đối chiếu số file, kích thước, checksum hoặc số bản ghi trước và sau. Lớp vận hành kiểm tra retry, log, thời gian chạy và mức sử dụng tài nguyên. Lớp bảo mật xác nhận service không chạy bằng root nếu không cần thiết và file nhạy cảm không mở quyền cho group hoặc other.

set -euo pipefail
test -d ~/linux-lab/logs
test -s ~/linux-lab/evidence/session.txt
find ~/linux-lab -type f -perm -0002 -print | tee evidence/world_writable.txt
df -h | tee evidence/disk_after.txt
free -h | tee evidence/memory_after.txt

Một bài lab chỉ đạt khi lệnh có thể chạy lại mà không tạo trạng thái hỏng, lỗi trả mã thoát khác 0, log có timestamp và người khác làm theo README có thể tái tạo kết quả. Nếu kiểm thử chỉ dựa trên việc “màn hình không báo lỗi”, sản phẩm chưa đủ tiêu chuẩn production.

Những lỗi thường gặp và cách xử lý

Lỗi thường gặp gồm dùng khoảng trắng không quote, nhầm glob với regex, ghi đè file nguồn, parse ls, bỏ qua newline trong tên file và tạo pipeline che mất lỗi của lệnh đầu. Cách xử lý có hệ thống là tái hiện lỗi bằng đầu vào nhỏ, thu thập trạng thái bằng lệnh chỉ đọc, xác định lớp gây lỗi rồi mới thay đổi. Không sửa nhiều cấu hình cùng lúc vì sẽ mất khả năng xác định thay đổi nào thực sự giải quyết vấn đề.

Khi cần trợ giúp, cung cấp phiên bản hệ điều hành, câu lệnh đầy đủ đã che dữ liệu nhạy cảm, output, mã thoát, quyền của file liên quan và log trong đúng khoảng thời gian xảy ra lỗi. Mô tả tốt giúp rút ngắn thời gian điều tra và tránh các lời khuyên nguy hiểm như mở quyền 777 hoặc tắt firewall.

Đưa kỹ năng vào môi trường production

Pipeline command line cần set locale, working directory, umask, error handling và log. Dữ liệu nguồn được đọc bất biến; output ghi vào đường dẫn tạm rồi rename nguyên tử khi hoàn tất.

Trước mỗi thay đổi, cần có ticket, phạm vi ảnh hưởng, cửa sổ triển khai, backup, tiêu chí thành công, tiêu chí rollback và người chịu trách nhiệm. Sau thay đổi, lưu evidence, cập nhật runbook và theo dõi ít nhất một chu kỳ workload. Linux ổn định không đến từ một câu lệnh thần kỳ mà từ quy trình có thể kiểm chứng.

Bài tập mở rộng

  1. Viết một script chỉ đọc để thu thập trạng thái liên quan đến chủ đề và xuất báo cáo có timestamp.
  2. Tạo một lỗi có kiểm soát trong máy ảo, ghi triệu chứng, giả thuyết, bằng chứng và cách khôi phục.
  3. Chuyển ba thao tác thủ công thành script có set -euo pipefail và kiểm tra đầu vào.
  4. Viết runbook một trang để người khác thực hiện mà không cần hỏi lại tác giả.
  5. Đánh giá thay đổi cần thiết nếu workload tăng gấp 100 lần.

Tài liệu tham khảo

  • Ubuntu Server Documentation.
  • GNU Coreutils và Bash Reference Manual.
  • Linux man-pages project.
  • systemd và OpenSSH documentation, sử dụng theo nội dung từng bài.

Kết luận

Khi hiểu dòng dữ liệu qua stdin, stdout và pipe, command line trở thành môi trường xử lý dữ liệu nhẹ, nhanh và minh bạch.

TechData.AI - Leading the Future.
Tham khảo các khoá học theo link: https://techdata.ai/techdata-ai-course/
Hoàng Minh.

Scroll to Top