Tự động hóa các lời nhắc CLI tương tác với Expect: Hướng dẫn cho quản trị viên Linux

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Rào cản từ các lời nhắc tương tác

Việc viết shell script tiêu chuẩn rất tuyệt vời cho các tác vụ tuần tự, nhưng nó thường gặp trở ngại lớn khi một lệnh yêu cầu tương tác theo thời gian thực.

Tôi đang nói về những khoảnh khắc khi một script đột ngột dừng lại, chờ bạn nhập mật khẩu, xác nhận thỏa thuận cấp phép hoặc chọn giữa ‘yes’ (có) và ‘no’ (không). Các lệnh như <a href="https://itnotes.dev/vi/dung-de-mat-tien-trinh-ssh-huong-dan-thuc-hanh-tmux-cho-quan-tri-vien-linux/">ssh</a>, ftp, hoặc thậm chí là các trình cài đặt ứng dụng tùy chỉnh thường phớt lờ việc chuyển hướng đầu vào tiêu chuẩn (như echo "password" | command) vì chúng tìm kiếm cụ thể một phiên TTY (teletype) để đảm bảo bảo mật.

Đây là lúc Expect trở thành “cứu cánh”. Được phát triển từ đầu những năm 90, Expect là một công cụ được xây dựng trên ngôn ngữ Tcl, thiết kế riêng để giao tiếp với các chương trình tương tác. Nó theo dõi một chuỗi cụ thể (lời nhắc – “prompt”) và sau đó gửi phản hồi đã được định nghĩa trước. Nó giống như việc có một robot ảo ngồi tại terminal và gõ phím thay bạn. Tôi thấy nó không thể thiếu đối với các hệ thống cũ không có API và không hỗ trợ khóa SSH.

Cài đặt Expect trên hệ thống của bạn

Trước khi bắt đầu tự động hóa, chúng ta cần công cụ này. Expect không phải lúc nào cũng được cài đặt sẵn trên các bản phân phối Linux tối giản, nhưng nó có sẵn trong hầu hết các kho lưu trữ chính. Việc cài đặt rất đơn giản và mất chưa đầy một phút.

Trên Debian, Ubuntu, hoặc Linux Mint:

sudo apt update
sudo apt install expect -y

Trên RHEL, Fedora, hoặc AlmaLinux:

sudo dnf install expect -y

Để xác nhận cài đặt, bạn chỉ cần kiểm tra phiên bản. Hệ thống hiện tại của tôi đang chạy phiên bản 5.45.4, hoạt động rất ổn định cho tất cả những gì chúng ta sắp thực hiện.

expect -v

Viết script Expect đầu tiên của bạn

Một script Expect thường bắt đầu bằng dòng shebang trỏ đến trình thông dịch Expect. Mặc dù nó kế thừa cú pháp từ Tcl, bạn không cần phải là một chuyên gia Tcl mới có thể viết được các script tự động hóa hiệu quả. Hầu hết các script xoay quanh bốn lệnh chính: spawn, expect, send, và interact.

Các lệnh cốt lõi: Spawn, Expect và Send

Hãy xem một ví dụ kinh điển: tự động hóa đăng nhập SSH. Đây là một vấn đề phổ biến khi làm việc với các thiết bị mạng cũ không hỗ trợ xác thực bằng public key.

#!/usr/bin/expect -f

# Thiết lập biến
set timeout 10
set host "192.168.1.50"
set user "admin"
set password "Secret123!"

# Bắt đầu tiến trình
spawn ssh $user@$host

# Tìm kiếm lời nhắc mật khẩu
expect "password:"

# Gửi mật khẩu kèm theo ký tự xuống dòng
send "$password\r"

# Trao quyền điều khiển cho người dùng
interact

Trong đoạn mã này, spawn khởi động lệnh SSH. Lệnh expect chờ chuỗi “password:” xuất hiện trong terminal. Khi tìm thấy, send sẽ đẩy chuỗi mật khẩu kèm theo \r (mô phỏng phím Enter). Cuối cùng, interact giữ cho phiên làm việc tiếp tục để bạn có thể điều khiển thủ công; nếu không có nó, script sẽ kết thúc và phiên SSH sẽ đóng ngay lập một.

Xử lý nhiều lời nhắc và logic

Các tình huống thực tế hiếm khi đơn giản như vậy. Thông thường, bạn sẽ gặp các lời nhắc khác nhau tùy thuộc vào việc đây là lần đầu tiên bạn kết nối hay có lỗi xảy ra. Tôi ưu tiên sử dụng cấu trúc giống như switch bên trong khối expect để xử lý các biến thể này.

spawn ssh $user@$host

expect {
    "yes/no" {
        send "yes\r"
        exp_continue
    }
    "password:" {
        send "$password\r"
    }
    timeout {
        puts "Kết nối quá thời gian chờ!"
        exit 1
    }
}

Lệnh exp_continue là một mẹo nhỏ hay ho. Nó yêu cầu Expect tiếp tục chờ thêm các lời nhắc sau khi đã gửi phản hồi. Điều này cho phép bạn xử lý việc xác nhận dấu vân tay SSH (“yes/no”) và sau đó xử lý ngay lời nhắc mật khẩu theo sau.

Truyền tham số từ Shell

Việc viết cứng (hardcoding) mật khẩu và tên máy chủ là một thói quen xấu. Bạn có thể truyền tham số từ shell Bash vào script Expect bằng cách sử dụng mảng argv. Điều này giúp các script của bạn có thể tái sử dụng trong các môi trường khác nhau.

#!/usr/bin/expect -f

set user [lindex $argv 0]
set host [lindex $argv 1]
set password [lindex $argv 2]

spawn ssh $user@$host
expect "password:"
send "$password\r"
interact

Bạn sẽ gọi script này như sau: ./myscript.exp admin 10.0.0.1 mypass. Hãy cẩn thận với cách tiếp cận này, vì mật khẩu được truyền dưới dạng đối số có thể hiển thị trong lịch sử shell của bạn.

Kiểm tra script và đảm bảo tính tin cậy

Tự động hóa chỉ hữu ích nếu nó đáng tin cậy. Tôi đã thấy nhiều script thất bại vì chúng quá “giòn” (brittle) — nghĩa là chúng mong đợi một lời nhắc nhưng lời nhắc đó đã thay đổi một chút do cập nhật phần mềm hoặc cài đặt màu sắc terminal khác nhau.

Gỡ lỗi với exp_internal

Khi một script Expect bị treo, thường là do nó đang chờ một chuỗi ký tự không bao giờ xuất hiện. Để xem chính xác những gì Expect đang thấy, hãy thêm exp_internal 1 vào đầu script. Điều này sẽ kết xuất quá trình khớp lệnh nội bộ ra terminal, cho bạn thấy chính xác lỗi không khớp xảy ra ở đâu.

Thiết lập thời gian chờ (timeout) thực tế

Theo mặc định, Expect có thời gian chờ là 10 giây. Đối với các tác vụ chạy lâu như di chuyển cơ sở dữ liệu hoặc cài đặt từ xa, thời gian này thường quá ngắn. Bạn có thể đặt timeout toàn cục bằng set timeout 300 (5 phút) hoặc sử dụng -1 để chờ vô thời hạn. Tuy nhiên, chờ vô thời hạn là rủi ro; nếu chương trình bị lỗi, script của bạn sẽ bị treo mãi mãi.

Tầm quan trọng của việc kiểm tra cục bộ

Sau khi quản lý hơn 10 máy chủ ảo (VPS) Linux trong hơn 3 năm, tôi học được rằng phải luôn kiểm tra kỹ lưỡng trước khi áp dụng vào môi trường thực tế (production). Điều này đặc biệt đúng với các script Expect. Một lỗi đánh máy nhỏ trong chuỗi ký tự mong đợi có thể dẫn đến việc script nhập sai mật khẩu nhiều lần, gây khóa bảo mật trên máy chủ của bạn. Tôi luôn khuyên bạn nên kiểm tra trên máy ảo cục bộ hoặc container staging trước.

Giải pháp thay thế: Sử dụng Autoexpect

Nếu bạn đang đối mặt với một công cụ tương tác đặc biệt phức tạp, việc viết script thủ công có thể rất tẻ nhạt. Có một công cụ tên là autoexpect hoạt động giống như một trình ghi macro. Bạn chạy autoexpect ./your_command, thực hiện các bước tương tác theo cách thủ công và nó sẽ tạo ra một tệp script.exp cho bạn. Nó thường hơi lộn xộn và có quá nhiều chú thích, nhưng là một điểm khởi đầu tuyệt vời cho các quy trình phức tạp.

Việc tích hợp Expect vào bộ công cụ của bạn sẽ thay đổi cách bạn quản lý Linux. Nó thu hẹp khoảng cách giữa can thiệp thủ công và tự động hóa hoàn toàn, cho phép bạn mở rộng quy trình công việc mà không bị cản trở bởi các lời nhắc tương tác kiểu cũ. Chỉ cần nhớ lưu ý đến vấn đề bảo mật — hãy bảo vệ quyền hạn của script (chmod 700) và tránh sử dụng mật khẩu văn bản thuần túy bất cứ khi nào có các lựa chọn thay thế an toàn hơn như khóa SSH hoặc token bảo mật.

Share: