Lệnh Bash printf cho phép bạn khả năng kiểm soát Terminal tốt hơn và nhiều tùy chọn định dạng hơn lệnh echo. Vậy nên, trong bài viết này, mình sẽ hướng dẫn các bạn cách sử dụng lệnh Bash printf trên Linux nhé.
Printf trên Terminal
Đây là một trong những phần cơ bản nhất của việc tương tác với một chương trình. Chương trình ghi một cái gì đó lên màn hình và bạn đọc nó. Ngay cả khi xem xét quy ước về các chương trình dòng lệnh bắt nguồn từ Unix và Linux thì nó càng ngắn gọn càng tốt — nhiều chương trình chỉ ghi lỗi vào Terminal nếu có sự cố. Nói cho người dùng biết những gì đang xảy ra, hoặc sắp xảy ra, hoặc vừa mới xảy ra là một nguyên tắc lập trình thiết yếu.
Bash shell có lệnh echo có thể ghi văn bản vào Terminal. Nó có thể xử lý các biến và hiển thị giá trị của chúng nếu chúng được in dưới dạng chuỗi và bạn có thể sử dụng nó trong các tập lệnh hoặc trên dòng lệnh. Vậy tại sao printf lại tồn tại? Echo có bị vấn đề khi viết văn bản không? Chà, printf cung cấp các chức năng khác ngoài hành động đơn giản là ghi chuỗi văn bản vào Terminal. Nó cho phép bạn định dạng đầu ra với tính linh hoạt cao và nó cũng có các công dụng khác.
Lệnh Bash printf được mô phỏng trên hàm printf từ ngôn ngữ C, nhưng nó vẫn có chút khác biệt. Nếu bạn biết C, bạn sẽ cần phải để ý những điểm khác biệt đó.
Viết các chuỗi cơ bản
Hãy xem echo và printf khác nhau như thế nào khi chúng in các chuỗi ra terminal.
echo here are some words
printf here are some words
Lệnh echo in tất cả các từ nhưng printf chỉ in từ đầu tiên. Ngoài ra, không có dòng mới nào được in ra. Kết quả đầu ra được đặt ngay trên dấu nhắc lệnh. Tuy nhiên, điều đầu tiên, để printf hoạt động được với tất cả các từ, thì bạn cần đặt chuỗi trong dấu nháy kép.
echo here are some words
printf "here are some words"
Printf đã in tất cả các từ nhưng chúng ta vẫn không nhận được dòng mới. Đó là bởi vì với printf, bạn chỉ nhận được một dòng mới nếu bạn yêu cầu. Điều đó nghe có vẻ như là một khuyết điểm nhưng nó cho phép bạn quyết định có nên thêm dòng mới hay không. Để khiến printf in một dòng mới, bạn cần thêm“\ n” vào chuỗi của mình.
echo here are some words
printf "here are some words\n"
Đôi khi bạn sẽ sử dụng một dòng mới và đôi khi thì không. Đây là trường hợp một câu lệnh printf sử dụng một dòng mới còn câu lệnh kia thì không.
printf "How-To " && printf "Geek\n"
Bởi vì lệnh printf đầu tiên không in một dòng mới, nên đầu ra từ printf thứ hai được đặt ngay sau “How-To” và trên cùng một dòng. Printf thứ hai sử dụng \n để in một dòng mới. Điều này làm cho dấu nhắc lệnh xuất hiện trên dòng bên dưới văn bản được in.
Các ký tự đặc biệt khác
Dưới đây là một số ký tự đặc biệt khác mà bạn có thể sử dụng như “\n”.
- \n: Tạo một dòng mới.
- \r: In văn bản ra đầu dòng và gửi con trỏ về vị trí hiện tại.
- \t: In một ký tự tab.
- \v: in không gian tab dọc.
- \\: In một ký tự gạch chéo ngược.
- \”: In một ký tự nháy kép.
- \b: In dấu cách.
Ký tự thoát xuống dòng và trả con trỏ trở lại vị trí hiện tại.
printf "Honey is the root of all evil\rMoney\n"
Lệnh printf xử lý đầu vào của nó từ trái sang phải. Chuỗi được in dưới dạng văn bản bình thường cho đến khi printf gặp ký tự “\r”. Con trỏ đầu ra được chuyển trở lại đầu dòng hiện tại.
Khi lệnh printf gặp ký tự “\r” thì nó sẽ in văn bản sau ký tự “\r” tức là Money đè lên Honey và trả con trỏ về vị trí hiện tại. Tức là con trỏ lúc này sẽ ở cuối chữ evil.
Dấu nháy kép “”” được sử dụng để trích dẫn các chuỗi và ký tự gạch chéo ngược “\” biểu thị các văn bản thuần. Nếu bạn muốn in các ký tự này, bạn cần loại bỏ chúng bằng một dấu gạch chéo ngược. Điều này khiến printf coi chúng là các ký tự chữ bình thường.
printf "This is a \tTab, this is a quotation mark \", and this \\ is a Backslash\n"
Sử dụng các biến
Sử dụng các biến với printf rất giống với echo. Để bao gồm một biến, như biến môi trường này, hãy đặt trước nó ký hiệu đô la “$” như bình thường.
printf "Home directory: $HOME\n"
Định dạng chuỗi
Định dạng chuỗi là xác định định dạng đầu ra của chuỗi. Bạn cung cấp văn bản và các giá trị khác làm đối số để định dạng chuỗi.
Chuỗi định dạng có thể bao gồm văn bản, chuỗi ký tự đặc biệt và mã định dạng. Các giá trị định dạng cho printf biết loại đối số, chẳng hạn như chuỗi, số nguyên hoặc ký tự.
Đây là những định dạng phổ biến nhất. Tất cả chúng đều được bắt đầu bằng dấu phần trăm “%”. Để in dấu phần trăm, bạn sử dụng hai dấu phần trăm cùng nhau “%%”.
- %s: In một chuỗi.
- %c: In một ký tự.
- %d: In một số nguyên.
- %f: in ra một số thực.
- %u: In một số nguyên không dấu.
- %o: In một giá trị dưới dạng bát phân.
- %x: In một giá trị ở dạng thập lục phân, ở dạng chữ thường.
- %X: In một giá trị ở dạng thập lục phân, ở dạng chữ hoa.
- %e: In một số thực dưới dạng ký hiệu khoa học, ở dạng chữ thường.
- %E: In một số thực trong ký hiệu khoa học, ở dạng chữ hoa.
- %%: In biểu tượng phần trăm “%”.
printf "How-To %s\n" "Geek"
printf "%s%s %s\n" "How" "-To" "Geek"
Định dạng chuỗi trong lệnh đầu tiên bao gồm một số văn bản của riêng nó. Mình chuyển chuỗi “Geek” làm đối số cho printf. Nó được in dưới định dạng “% s”. Lưu ý rằng chỉ có một khoảng cách giữa chuỗi định dạng và đối số. Trong C, bạn cần dấu phẩy để phân tách chúng nhưng với phiên bản Bash của printf, sử dụng khoảng trắng là đủ.
Chuỗi định dạng thứ hai chỉ chứa các mã định dạng và chuỗi văn bản mới. Ba đối số chuỗi lần lượt được sử dụng bởi từng từ định dạng định dạng “%s”. Một lần nữa, trong C, bạn cần đặt dấu phẩy giữa mỗi đối số nhưng Bash printf cho phép chúng ta quên điều đó.
Để in các loại đối số khác nhau, bạn chỉ cần sử dụng công cụ định dạng thích hợp. Đây là một quy trình chuyển đổi số nhanh được xây dựng bằng printf. Mình sẽ in giá trị 15 dưới dạng ký hiệu thập phân, bát phân và thập lục phân.
printf "Dec: %d\nOct: %o\nHex: %x\n" 15 15 15
Một ví dụ khác bớt lộn xộn hơn.
printf "Hex: %x\n" 15
Hầu hết chúng ta đã quen với việc nhìn thấy các giá trị thập lục phân ở dạng chữ hoa và với các giá trị nhỏ hơn 0x10 được in với số 0 ở đầu. Chúng ta có thể làm vậy bằng cách sử dụng mã định dạng thập lục phân viết hoa “%X” và đặt một mã xác định độ rộng giữa dấu phần trăm “%” và ký tự “X”.
Điều này cho printf biết chiều rộng của trường mà đối số sẽ được in. Trường này được đệm bằng khoảng trắng. Với định dạng này, các giá trị có hai chữ số sẽ được in mà không có bất kỳ phần đệm nào.
printf "Hex: %2X\n" 15
Bây giờ chúng ta nhận được một giá trị chữ hoa, được in với khoảng trắng ở đầu. Chúng ta có thể làm cho printf in số 0 thay vì khoảng trắng bằng cách đặt một số 0 ở trước 2:
printf "Hex: %02X\n" 15
Bộ chỉ định độ chính xác cho phép bạn đặt số lượng dấu thập phân để in ra.
printf "Floating point: %08.3f\n" 9.243546
Điều này giúp bạn dễ dàng tạo ra các bảng kết quả với đầu ra được căn chỉnh gọn gàng. Lệnh tiếp theo này cũng thể hiện một trong những điều kỳ quặc khác của Bash printf. Nếu có nhiều đối số hơn số định dạng, các đối số sẽ được đưa vào chuỗi định dạng theo thứ tự cho đến khi tất cả các đối số được sử dụng hết.
printf "Float: %8.3f\n" 9.243546 23.665 8.0021
Bạn cũng có thể sử dụng các ký tự chỉ định độ rộng và độ chính xác với các chuỗi. Lệnh này in các chuỗi trong một trường rộng 10 ký tự.
printf "%10s %d\n" "coats" 7 "shoes" 22 "Umbrellas" 3
Theo mặc định, các giá trị được căn phải trong các trường của chúng. Để căn trái chúng, hãy sử dụng dấu trừ “-” ngay sau dấu phần trăm “%”.
printf "%-10s %d" "coats" 7 "shoes" 22 "Umbrellas" 3
Bộ chỉ định độ chính xác có thể được sử dụng để đặt số lượng ký tự tối đa được in. Chúng ta đang sử dụng các ký tự dấu hai chấm “:” để hiển thị giới hạn của trường chiều rộng.
printf ":%10.6s:\n" "coats" "shoes" "Umbrellas"
printf ":%-10.6s:\n" "coats" "shoes" "Umbrellas"
Thông số chiều rộng thậm chí có thể được chuyển vào dưới dạng đối số. Sử dụng dấu hoa thị “*” thay vì ký hiệu số và chuyển chiều rộng làm đối số số nguyên.
printf "%*s\n" 20 "Rightmost" 12 "Middle" 5 "leftmost"
Các thủ thuật khác
Các chỉ định định dạng bên trong chuỗi định dạng sẽ hoạt động với các giá trị thích hợp, cho dù chúng được cung cấp trên dòng lệnh dưới dạng đối số thông thường hay chúng được tạo dưới dạng đầu ra của một biểu thức.
Lệnh này in ra tổng của hai số:
printf "23+32=%d\n" $((23+32))
Lệnh này in ra số lượng thư mục trong thư mục làm việc hiện tại:
printf "There are %d directories\n" $(ls -d */ | wc -l)
Lệnh printf này in một chuỗi được trả về từ một lệnh khác.
printf "Current user: %s\n" $(whoami)
Nếu mã định dạng chuỗi “%s” không được cung cấp cùng với một đối số thì printf sẽ không in ra.
printf "One: %s two: %s\n" "Alpha"
Nếu mã định dạng chuỗi “%s” được cung cấp một giá trị số, thì nó vẫn in số đó ra.
printf "One: %s two: %s\n" "Alpha" 777
Nếu mã định dạng số nguyên “%d” không nhận được đối số, nó sẽ in 0.
printf "Integer: %d\n"
Nếu một định dạng số nguyên “%d” nhận được một đối số chuỗi, Bash sẽ in một thông báo lỗi và printf sẽ in ra số 0.
printf "Integer: %d\n" "Seven"
Các ký hiệu khó hiểu có thể được tạo ra bằng cách sử dụng Unicode hoặc “điểm mã” của chúng. Chúng được tạo bằng chữ cái “u” theo sau là giá trị Unicode của chúng.
printf "The Euro symbol: \u20AC\n"
Để bao gồm trình tự thoát trong chuỗi đối số, bạn phải sử dụng trình chỉ định định dạng “%b” trong chuỗi định dạng, không phải từ định dạng chuỗi “%s”.
printf "%s" "\u20AC\n"
printf "%b" "\u20AC\n"
Câu lệnh printf đầu tiên không xử lý giá trị Unicode và nó không nhận dạng ký tự tạo dòng mới. Câu lệnh printf thứ hai sử dụng mã định dạng “%b”. Điều này xử lý chính xác ký tự Unicode và một dòng mới được in.