Sau khi tìm hiểu về lệnh echo, chúng ta sẽ khám phá một công cụ xuất khác mạnh mẽ hơn trong Shell là lệnh printf.
Lệnh printf được thiết kế để hoạt động tương tự như hàm printf() trong thư viện chuẩn của ngôn ngữ C. Nó tuân thủ tiêu chuẩn POSIX, do đó, các script sử dụng printf thường có tính di động (portability) tốt hơn so với các script chỉ dùng echo.
Một điểm khác biệt quan trọng là printf sử dụng các chuỗi định dạng và các đối số riêng biệt, cho phép kiểm soát chi tiết về cách dữ liệu được hiển thị, bao gồm độ rộng trường, căn chỉnh trái/phải và định dạng số thập phân. Không giống như echo, printf không tự động thêm ký tự xuống dòng (newline) ở cuối mỗi lần xuất. Bạn cần thêm thủ công ký tự \n nếu muốn.
Cú pháp của Lệnh `printf`
printf chuỗi_định_dạng [đối_số...]
Trong đó:
chuỗi_định_dạng: Là một chuỗi chứa các chỉ định định dạng (format specifiers) và văn bản thông thường.đối_số: Là danh sách các giá trị sẽ được chèn vào chuỗi định dạng theo thứ tự.
Ví dụ cơ bản
So sánh cách echo và printf xuất văn bản:
$ echo "Chào bạn, đây là Shell!"
Chào bạn, đây là Shell!
$ printf "Chào bạn, đây là Shell!\n"
Chào bạn, đây là Shell!
$
Định dạng dữ liệu có cấu trúc
printf thực sự tỏa sáng khi bạn cần xuất dữ liệu theo cột với định dạng nhất quán. Hãy xem xét ví dụ sau để tạo một bảng đơn giản:
#!/bin/bash
# Định dạng tiêu đề cột
printf "%-15s %-10s %-8s\n" "Sản Phẩm" "Số Lượng" "Giá Thành"
# Định dạng dữ liệu sản phẩm
printf "%-15s %-10d %-8.2f\n" "Ổ Cứng SSD" 5 125.50
printf "%-15s %-10d %-8.2f\n" "RAM DDR4" 12 75.99
printf "%-15s %-10d %-8.2f\n" "CPU Intel i7" 3 320.00
Khi chạy script này, bạn sẽ nhận được đầu ra được căn chỉnh đẹp mắt:
Sản Phẩm Số Lượng Giá Thành
Ổ Cứng SSD 5 125.50
RAM DDR4 12 75.99
CPU Intel i7 3 320.00
Trong ví dụ trên:
%s,%d,%flà các chỉ định định dạng tương ứng cho chuỗi (string), số nguyên (decimal integer) và số thực dấu phẩy động (floating-point number).%-15s: Định dạng một chuỗi với độ rộng 15 ký tự, căn chỉnh trái (dấu-). Nếu chuỗi ngắn hơn 15 ký tự, nó sẽ được đệm bằng khoảng trắng. Nếu dài hơn, toàn bộ chuỗi vẫn sẽ được hiển thị.%-8.2f: Định dạng một số thực với độ rộng tổng cộng 8 ký tự (bao gồm dấu thập phân và các chữ số), căn chỉnh trái, và hiển thị chính xác 2 chữ số sau dấu thập phân.
Các ví dụ định dạng khác
printf có thể xử lý nhiều tình huống khác nhau về chuỗi định dạng và đối số:
#!/bin/bash
# Chuỗi định dạng dùng dấu nháy kép
printf "%d - %s\n" 20 "chuỗi thứ nhất"
# Chuỗi định dạng dùng dấu nháy đơn (kết quả tương tự)
printf '%d - %s\n' 21 "chuỗi thứ hai"
# Không cần dấu nháy cho chuỗi định dạng đơn giản
printf %s "không cần nháy"
# Khi có nhiều đối số hơn chỉ định định dạng, chuỗi định dạng sẽ được tái sử dụng
printf "%s " "alpha" "beta" "gamma" "delta"
printf "\n"
# Mỗi chỉ định định dạng mới một dòng
printf "%s\n" "dòng một" "dòng hai"
# Khi thiếu đối số: %s sẽ được thay bằng NULL (rỗng), %d sẽ được thay bằng 0
printf "Giá trị chuỗi: '%s', Giá trị số: '%d'\n"
Kết quả đầu ra của script trên:
20 - chuỗi thứ nhất
21 - chuỗi thứ hai
không cần nháyalpha beta gamma delta
dòng một
dòng hai
Giá trị chuỗi: '', Giá trị số: '0'
Các chuỗi thoát (Escape Sequences) của printf
printf hỗ trợ một số chuỗi thoát đặc biệt để chèn các ký tự không in được hoặc có ý nghĩa đặc biệt:
| Chuỗi thoát | Mô tả |
|---|---|
\a |
Ký tự cảnh báo (bell), thường là BEL của ASCII. |
\b |
Ký tự lùi về trước một vị trí (backspace). |
\c |
Ngừng xuất đầu ra tại đây; mọi ký tự còn lại trong chuỗi đối số, các đối số tiếp theo và phần còn lại của chuỗi định dạng đều bị bỏ qua. |
\f |
Ký tự xuống trang (form feed). |
\n |
Ký tự xuống dòng mới (newline). |
\r |
Ký tự về đầu dòng (carriage return). |
\t |
Ký tự tab ngang. |
\v |
Ký tự tab dọc. |
\\ |
Một ký tự dấu gạch chéo ngược (backslash) theo nghĩa đen. |
\ddd |
Ký tự có giá trị bát phân (octal) 1 đến 3 chữ số (chỉ có hiệu lực trong chuỗi định dạng). |
\0ddd |
Ký tự có giá trị bát phân 1 đến 3 chữ số. |
Ví dụ về Chuỗi thoát
$ printf "Chuỗi thông thường (không xử lý):<%s>\n" "Dòng 1\nDòng 2"
Chuỗi thông thường (không xử lý):<Dòng 1\nDòng 2>
$ printf "Chuỗi được xử lý (với %%b):<%b>\n" "Dòng A\nDòng B"
Chuỗi được xử lý (với %b):<Dòng A
Dòng B>
$ printf "Thông báo quan trọng \a"
Thông báo quan trọng $ # Ký tự \a sẽ phát ra tiếng chuông (nếu terminal hỗ trợ)
Lưu ý sự khác biệt giữa %s và %b: %s coi các chuỗi thoát như văn bản thông thường, trong khi %b diễn giải và xử lý chúng.
Chi tiết về các Chỉ định Định dạng: %d, %s, %c, %f
%d: Decimal (Số nguyên thập phân)
Đối số tương ứng phải là một số nguyên thập phân. Nếu không,printfsẽ báo lỗi hoặc đưa ra kết quả không mong muốn.%s: String (Chuỗi ký tự)
Đối số tương ứng phải là một chuỗi ký tự.%c: Character (Ký tự)
Đối số tương ứng phải là một ký tự hoặc chuỗi ký tự. Nếu là chuỗi,printfchỉ lấy ký tự đầu tiên của chuỗi đó.%f: Float (Số thực dấu phẩy động)
Đối số tương ứng phải là một giá trị số (có thể là số nguyên hoặc số thực).
Ví dụ về việc %c chỉ lấy ký tự đầu tiên:
$ printf "Số: %d, Chuỗi: %s, Ký tự: %c\n" 42 "Hello World" "Zyxwv"
Số: 42, Chuỗi: Hello World, Ký tự: Z