TopDev

Khám Phá I/O Redirection và Pipelines – Biến Bạn Thành Phù Thủy Dữ Liệu!

minhdev 📖 10 phút đọc

Bạn có bao giờ muốn "bắt" kết quả của một lệnh, hoặc "truyền" dữ liệu từ lệnh này sang lệnh khác một cách mượt mà? Nếu có, bạn đang đứng trước cánh cửa của I/O Redirection (chuyển hướng vào/ra) và Pipelines (ống dẫn) – hai tính năng "bí mật" nhưng cực kỳ mạnh mẽ của Shell!

Đây là những kỹ thuật biến bạn từ người dùng Shell thông thường thành một "phù thủy" thực thụ, có khả năng tự động hóa và xử lý dữ liệu phức tạp chỉ bằng vài dòng lệnh. Hãy cùng TopDev khám phá 15 câu hỏi quan trọng nhất về chủ đề này!



I. Hiểu Rõ Standard Streams: Nền Tảng của Mọi Chuyển Hướng!#

Trước khi đi sâu vào chuyển hướng, bạn cần biết về ba "dòng" (streams) dữ liệu tiêu chuẩn mà mọi chương trình trên Linux/Unix đều sử dụng:

  • Standard Input (stdin) - File Descriptor 0: Đây là nơi chương trình nhận đầu vào. Mặc định, stdin được kết nối với bàn phím của bạn.

  • Standard Output (stdout) - File Descriptor 1: Đây là nơi chương trình gửi đầu ra thông thường. Mặc định, stdout được hiển thị trên màn hình (terminal).

  • Standard Error (stderr) - File Descriptor 2: Đây là nơi chương trình gửi thông báo lỗi. Mặc định, stderr cũng được hiển thị trên màn hình (terminal), nhưng tách biệt với stdout.

Khi bạn gõ lệnh và thấy kết quả trên màn hình, đó thường là stdout. Khi có lỗi, bạn sẽ thấy thông báo lỗi, đó là stderr.

II. I/O Redirection: Kiểm Soát Dòng Chảy Dữ Liệu!#

I/O Redirection cho phép bạn thay đổi nơi một lệnh nhận đầu vào (stdin) hoặc gửi đầu ra (stdout, stderr).

1. Input Redirection (<) - Lấy dữ liệu từ tệp thay vì bàn phím#

  • Công dụng: Chuyển hướng stdin của một lệnh từ bàn phím sang một tệp. Lệnh sẽ đọc dữ liệu từ tệp đó.

  • Cú pháp: command < file.txt

  • Ví dụ: Giả sử bạn có tệp names.txt với nội dung: Alice Bob Charlie Lệnh wc -l (đếm số dòng) thường nhận đầu vào từ bàn phím. Sử dụng < để nó đọc từ tệp: ` wc -l < names.txt

    Output: 3

    wc -l đọc 3 dòng từ names.txt

    `

2. Output Redirection (>, >>) - Lưu kết quả ra tệp#

  • Công dụng: Chuyển hướng stdout của một lệnh từ màn hình ra một tệp.

  • Dấu > (Overwrite): Ghi đè lên nội dung tệp nếu tệp đã tồn tại. Nếu tệp chưa có, nó sẽ tạo tệp mới. Cú pháp: command > file.txt

    • Ví dụ: ` ls -l > list_files.txt # Ghi danh sách tệp vào list_files.txt (ghi đè nếu đã tồn tại) cat list_files.txt # Xem nội dung tệp

`

  • Dấu >> (Append): Nối thêm nội dung vào cuối tệp nếu tệp đã tồn tại. Nếu tệp chưa có, nó sẽ tạo tệp mới. Cú pháp: command >> file.txt

    • Ví dụ: ` echo "This is a new log entry." >> application.log # Thêm dòng mới vào cuối tệp log cat application.log # Xem nội dung tệp, dòng mới được thêm vào cuối

`

3. Chuyển hướng cả stdout và stderr vào cùng một tệp#

Đôi khi, bạn muốn lưu lại cả kết quả bình thường và thông báo lỗi.

  • Cú pháp 1 (&>): Phổ biến trong Bash. command &> output_and_error.log

  • Cú pháp 2 (2>&1): Cách truyền thống và tương thích rộng hơn. 2> là chuyển hướng stderr, 1stdout. 2>&1 nghĩa là "chuyển hướng stderr đến cùng nơi mà stdout đang được chuyển hướng đến". command > output_and_error.log 2>&1

  • Ví dụ: find /nonexistent_path 2>&1 # Sẽ xuất lỗi của find ra stdout (và hiển thị trên màn hình) find /nonexistent_path > error_output.log 2>&1 # Ghi cả lỗi vào tệp

4. Bỏ qua output của một lệnh (/dev/null)#

  • /dev/null: Đây là một "thiết bị rỗng" đặc biệt trong Linux/Unix. Mọi dữ liệu được gửi đến nó sẽ bị loại bỏ, và mọi dữ liệu đọc từ nó sẽ trả về rỗng.

  • Công dụng: Thường dùng để loại bỏ các thông báo lỗi hoặc kết quả không mong muốn.

  • Ví dụ: Bỏ qua stdout: ` ls -l > /dev/null # Lệnh ls vẫn chạy, nhưng không có gì được hiển thị ra màn hình

`

- Bỏ qua `stderr`:

find /nonexistent_path 2> /dev/null # Lệnh find vẫn báo lỗi, nhưng thông báo lỗi không hiển thị

- Bỏ qua cả `stdout` và `stderr`:

command &> /dev/null # HOẶC command > /dev/null 2>&1

5. Phân biệt >2> trong Redirection#

  • > (hoặc 1>): Chuyển hướng Standard Output (stdout). Đây là đầu ra "bình thường" của lệnh.

  • 2>: Chuyển hướng Standard Error (stderr). Đây là đầu ra chứa các thông báo lỗi hoặc cảnh báo.

  • Ví dụ: Tạo một tệp script test.sh với nội dung: #!/bin/bash echo "This is normal output." echo "This is an error!" >&2 # Gửi ra stderr Chạy các lệnh sau: ./test.sh > output.log # Chỉ stdout đi vào output.log, stderr vẫn ra màn hình ./test.sh 2> error.log # Chỉ stderr đi vào error.log, stdout vẫn ra màn hình ./test.sh > output.log 2> error.log # stdout vào output.log, stderr vào error.log

III. Pipelines (|) - Chuỗi Lệnh Không Giới Hạn!#

Pipeline là một trong những tính năng mạnh mẽ nhất của Shell, cho phép bạn kết nối đầu ra của một lệnh với đầu vào của lệnh tiếp theo.

6. Pipeline là gì? (|)#

  • Công dụng: Dấu gạch đứng | (ký tự pipe) dùng để "dẫn" stdout của lệnh bên trái sang làm stdin cho lệnh bên phải.

  • Cú pháp: command1 | command2 | command3 ...

  • Ví dụ: Xem danh sách các tệp, sau đó phân trang kết quả bằng less: ` ls -l /usr/bin | less

(Thay vìls -lhiển thị toàn bộ lên màn hình, output của nó được chuyển qualess`, cho phép bạn cuộn từng trang)

7. Kết hợp grep với Pipeline để lọc kết quả của ls#

Đây là một trong những ví dụ kinh điển và hữu ích nhất của pipeline!

  • Công dụng: Lọc danh sách tệp/thư mục mà ls trả về, chỉ hiển thị những cái có tên phù hợp.

  • Ví dụ: ` ls -l | grep "my_project"

    Lệnh này sẽ hiển thị danh sách chi tiết tất cả tệp/thư mục, sau đó grep sẽ lọc ra những dòng chứa chuỗi "my_project".

    ` (Rất hữu ích khi bạn có quá nhiều tệp và muốn tìm nhanh một nhóm tệp cụ thể)

8. Sử dụng wc (word count) với Pipeline để đếm#

  • wc: Lệnh dùng để đếm số dòng, số từ, hoặc số ký tự. -l: đếm số dòng

    • -w: đếm số từ

    • -c: đếm số ký tự

  • Ví dụ: Đếm số tệp trong thư mục hiện tại: ` ls | wc -l

`

- Đếm số dòng trong tệp `my_document.txt` (sử dụng `cat` để đọc tệp rồi pipe sang `wc`):

cat my_document.txt | wc -l

9. Sử dụng sort với Pipeline để sắp xếp output#

  • sort: Lệnh dùng để sắp xếp các dòng văn bản theo thứ tự bảng chữ cái hoặc số.

  • Ví dụ: Sắp xếp danh sách tệp theo tên: ` ls | sort

`

- Sắp xếp các dòng trong một tệp và loại bỏ các dòng trùng lặp:

cat names.txt | sort | uniq

10. Sử dụng uniq với Pipeline để loại bỏ các dòng trùng lặp#

  • uniq: Lệnh dùng để lọc các dòng trùng lặp liên tiếp trong đầu vào. (Lưu ý: uniq chỉ hoạt động với các dòng trùng lặp KỀ NHAU. Vì vậy, thường dùng sau lệnh sort).

  • Ví dụ: Đếm số dòng duy nhất trong một tệp: ` cat server_logs.txt | sort | uniq -c | sort -nr # Giải thích: # cat server_logs.txt: Đọc nội dung tệp. # sort: Sắp xếp các dòng để đưa các dòng trùng lặp cạnh nhau. # uniq -c: Loại bỏ các dòng trùng lặp và đếm số lần xuất hiện của mỗi dòng duy nhất. # sort -nr: Sắp xếp kết quả cuối cùng theo số lượng (số dòng trùng lặp) theo thứ tự giảm dần.

`

11. Lệnh tee - Vừa hiển thị, vừa lưu vào tệp!#

  • tee: Lệnh đặc biệt cho phép bạn vừa hiển thị stdout ra màn hình, vừa ghi nó vào một tệp cùng lúc. Giống như một cái "T-junction" trong ống nước.

  • Cú pháp: command | tee file.txt

  • Ví dụ: ` ls -l | tee file_list.txt

    Output của ls -l sẽ vừa hiển thị trên màn hình, vừa được ghi vào file_list.txt

    **tee -a file.txt`**: Nối thêm vào tệp thay vì ghi đè.

IV. Kết Nối Lệnh Nâng Cao & Ví Dụ Thực Tế#

12. xargs - Kết nối output của lệnh này làm tham số cho lệnh khác (không dùng |)#

  • xargs: Một lệnh cực kỳ mạnh mẽ để xây dựng và thực thi các dòng lệnh từ stdin. Nó nhận các dòng từ stdin và biến chúng thành các tham số cho một lệnh khác. Rất hữu ích khi một lệnh không chấp nhận đầu vào từ pipeline hoặc cần xử lý từng mục một.

  • Cú pháp: command1 | xargs command2

  • Ví dụ: Tìm tất cả các tệp .bak và xóa chúng: ` find . -name "*.bak" | xargs rm # find tìm các tệp, output của find (danh sách tệp) được truyền cho xargs, # xargs sẽ lấy từng tên tệp và truyền nó làm tham số cho lệnh rm.

`

- Xem nội dung của nhiều tệp cùng lúc (nếu `cat` không hỗ trợ đọc từ pipe):

ls *.txt | xargs cat

13. Viết một lệnh sử dụng pipeline để hiển thị 5 tiến trình đang sử dụng nhiều RAM nhất.#

Đây là một ví dụ thực tế cho thấy sức mạnh của pipeline:

ps aux --sort -rss | head -n 6

  • ps aux: Hiển thị tất cả các tiến trình đang chạy (a: all users, u: user-oriented format, x: process with no controlling tty).

  • --sort -rss: Sắp xếp kết quả theo cột RSS (Resident Set Size - lượng RAM đang chiếm) theo thứ tự giảm dần (-).

  • | head -n 6: Lấy 6 dòng đầu tiên. Vì dòng đầu tiên là tiêu đề cột, nên bạn sẽ có tiêu đề và 5 tiến trình "ngốn" RAM nhất.



Lời Kết#

I/O Redirection và Pipelines là những "bí quyết" thực sự giúp bạn khai thác tối đa sức mạnh của Shell. Chúng cho phép bạn xâu chuỗi các lệnh lại với nhau, xử lý dữ liệu phức tạp, tự động hóa các tác vụ lặp đi lặp lại một cách hiệu quả.

Đừng ngần ngại thực hành các ví dụ trên và tự tạo ra các "chuỗi lệnh" của riêng mình. Khi bạn bắt đầu suy nghĩ theo hướng "làm thế nào để output của lệnh này trở thành input của lệnh kia?", bạn sẽ nhận ra một thế giới hoàn toàn mới về khả năng xử lý dữ liệu trong Shell!

Bài liên quan trong #Shell

✓ Đã sao chép link