Đường ống (Pipe), lọc (Filter) và chuyển hướng (Redirection) trên Linux Image Đường ống (Pipe), lọc (Filter) và chuyển hướng (Redirection) trên Linux

Bài viết này giới thiệu với các bạn về đường ống (Pipe), lọc (Filter) và chuyển hướng (Redirection) trên hệ điều hành Linux qua khái niệm và ví dụ dưới đây sẽ giúp cho các bạn có thể tự nghiên cứu và tự học Linux cơ bản một cách dễ dàng.

1. Đường ống (Pipe)

1.1. Giới thiệu về đường ống (pipe)

Đường ống (pipe) dùng để chuyển hướng trên hệ điều hành Linux, nó cho phép chúng ta sử dụng hai hoặc nhiều lệnh sao cho đầu ra của một lệnh đóng vai trò trực tiếp làm đầu vào của lệnh tiếp theo. Kết nối trực tiếp giữa các lệnh cho phép chúng hoạt động đồng thời và cho phép dữ liệu được truyền giữa chúng liên tục thay vì phải chuyển qua các tệp văn bản tạm thời hoặc qua màn hình hiển thị. Các đường ống là một chiều tức là luồng dữ liệu chuyển hướng từ trái sang phải qua đường ống.

Biểu tượng | biểu thị một đường ống.

Các đường ống (pipe) giúp chúng ta có thể kết hợp hai hoặc nhiều lệnh cùng lúc và chạy chúng liên tiếp. Cú pháp như sau:

lệnh_1 | lệnh_2 | lệnh_3 | ... | lệnh_n

Chúng ta thực hiện một số ví dụ bên dưới để dể hiểu hơn:

Ví dụ 1: Sử dụng đường ống để kết hợp lệnh cat và lệnh less để xem một tệp có nhiều trang:

Khi chúng ta sử dụng lệnh cat để xem tệp nhưng khi tệp có nhiều trang thì đầu ra sẽ nhanh chóng đến trang cuối cùng của tệp và chúng ta sẽ không thấy nội dung ở giữa.

[root@localhost ~]# cat /var/log/secure
May 25 07:57:04 localhost polkitd[6407]: Loading rules from directory /etc/polkit-1/rules.d
May 25 07:57:04 localhost polkitd[6407]: Loading rules from directory /usr/share/polkit-1/rules.d
May 25 07:57:04 localhost polkitd[6407]: Finished loading, compiling and executing 2 rules
May 25 07:57:04 localhost polkitd[6407]: Acquired the name org.freedesktop.PolicyKit1 on the system bus
May 25 07:57:10 localhost sshd[7155]: Server listening on 0.0.0.0 port 22.
May 25 07:57:10 localhost sshd[7155]: Server listening on :: port 22.
May 25 07:57:21 localhost sshd[7266]: Accepted password for root from 192.168.21.1 port 53093 ssh2
May 25 07:57:21 localhost sshd[7266]: pam_unix(sshd:session): session opened for user root by (uid=0)

Để có thể xem toàn bộ nội dung tệp chúng ta có thể chuyển hướng đầu ra của lệnh cat thành less như sau:

[root@localhost ~]# cat /var/log/secure | less

May 24 00:22:43 localhost polkitd[6727]: Loading rules from directory /etc/polkit-1/rules.d
May 24 00:22:43 localhost polkitd[6727]: Loading rules from directory /usr/share/polkit-1/rules.d
May 24 00:22:43 localhost polkitd[6727]: Finished loading, compiling and executing 2 rules
May 24 00:22:43 localhost polkitd[6727]: Acquired the name org.freedesktop.PolicyKit1 on the system bus
May 24 00:22:48 localhost sshd[7289]: Server listening on 0.0.0.0 port 22.
May 24 00:22:48 localhost sshd[7289]: Server listening on :: port 22.
May 24 04:17:15 localhost sshd[7415]: Accepted password for root from 192.168.21.1 port 49728 ssh2
May 24 04:17:16 localhost sshd[7415]: pam_unix(sshd:session): session opened for user root by (uid=0)
May 24 04:18:07 localhost unix_chkpwd[7461]: password check failed for user (root)
...

Chúng ta nhấn Enter để di chuyển sang từ dòng, nhấn space để di chuyển sang một trang và nhấn q để thoát.

Ví dụ 2: Sử dụng lệnh sort kết hợp lệnh uniq để sắp xếp một tệp và in các giá trị duy nhất như sau:

[root@localhost ~]# cat test.txt
apple
apple
apple
delay
delay
delay
nice
nice
rain
rain
safari
safari
[root@localhost ~]# cat test.txt | uniq
apple
delay
nice
rain
safari

Ví dụ 3: Sử dụng head và tail để in các dòng trong một tệp.

Lệnh này sẽ lấy 9 dòng đầu tiên của tệp /etc/passwd và từ 9 dòng trên chúng ta thực hiện lấy 4 dòng cuối cùng.

[root@localhost ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-bus-proxy:x:999:998:systemd Bus Proxy:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:998:997:User for polkitd:/:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:997:995::/var/lib/chrony:/sbin/nologin
dang:x:1000:1000::/home/dang:/bin/bash
[root@localhost ~]# cat /etc/passwd | head -9 | tail -5
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

Ví dụ 4: Sử dụng ls và find để liệt kê và in tất cả các dòng khớp trong một tệp.

[root@localhost ~]#  ls -l | find ./ -type f -name "*.txt" -exec grep "network" {} \;
This is a example of network
The network
The               network

Lệnh trên sẽ giúp chúng ta chọn các tệp có phần mở rộng .txt trong thư mục hiện tại và tìm kiếm từ network trong các tệp có đuôi .txt và in nội dung những tệp đó.

Ví dụ 5: Sử dụng lệnh cat, grep, tee và wc để thực hiện chọn từ apple trong file test.txt và lưu trữ chúng trong file2.txt và in tổng số dòng có từ khớp với apple.

[root@localhost ~]# cat test.txt
apple
apple
apple
delay
delay
delay
nice
nice
rain
rain
safari
safari
[root@localhost ~]# cat test.txt | grep "apple" | tee file2.txt | wc -l
3
[root@localhost ~]# cat file2.txt
apple
apple
apple

1.2. Phân biệt pipe thông thường và named pipe

Sự khác biệt giữa đường ống (pipe) thông thường và đường ống có tên (named pipe). Đường ống có tên (named pipe) có sự hiện diện trong hệ thống tệp hiển thị dưới dạng file chúng không giống các file thông thường và chúng sẽ không có nội dung. Cho dù bạn có ghi nhiều dữ liệu vào một đường ống có tên (named pipe), thì file dường như trống.

Việc sử dụng đường ống có tên (named pipe) rất hữu ích khi thực hiện di chuyển dữ liệu giữa các tiến trình. Dữ liệu của một đường ống có tên được chứa trong bộ nhớ chứ không phải được ghi vào đĩa. Đường ống có tên chỉ được thông qua khi cả hai đầu của ống đã được mở. Chúng ta có thể nạp dữ liệu nào một đường ống nhiều lần trước khi mở ở đầu kia và đọc. Bằng cách sử dụng các đường ống được có tên, bạn có thể thiết lập một tiến trình ghi vào một đường ống và một tiến trình khác đọc từ đường ống.

Thiết lập một đường ống có tên (named pipe)

Chúng ta sử dụng lệnh mkfifo để tạo ra một file cho đường ống có tên (named pipe).

[root@localhost ~]# mkfifo my_pipe
[root@localhost ~]# ls -l mypipe
prw-r--r--. 1 root root 0 May 25 11:36 mypipe

Lưu ý: Loại tệp đặc biệt có chỉ định là p và độ dài của tệp bằng không.

Trong các hệ thống Linux cũ, đường ống có tên (named pipe) được tạo bằng lệnh mknod, và nằm trong thư mục /etc.

Chúng ta có thể ghi vào đường ống có tên bằng cách chuyển hướng đầu ra đến nó và độ dài của nó vẫn bằng không.

[root@localhost ~]# echo "Hello my_pipe" > my_pipe
[root@localhost ~]# ls -l mypipe
prw-r--r--. 1 root root 0 May 25 11:36 mypipe

Chúng ta sẽ đổ dữ liệu đường ống bằng cách chạy lệnh sau:

[root@localhost ~]# echo "Hello my_pipe" > my_pipe

Văn bản của chúng ta đã được đỗ vào đường ống. Bạn có thể ở đầu ra và đọc dữ liệu được đổ vào từ đường ống như sau:

[root@localhost ~]# cat my_pipe
Hello my_pipe

Một cách khác để xem cách một ống có tên hoạt động là thực hiện cả hai thao tác (đổ dữ liệu vào ống và lấy nó ở đầu kia) bằng cách đặt phần đổ vào nền.

[root@localhost ~]# echo "Hello my_pipe" > my_pipe &
[1] 7502
[root@localhost ~]# cat my_pipe
Hello my_pipe
[1]+  Done                    echo "Hello my_pipe" > my_pipe

Ví dụ: Bạn có thể thiết lập một tiến trình chỉ đơn giản là chờ dữ liệu xuất hiện ở đầu ra của ống. Chúng ta sử dụng lệnh tail để chờ dữ liệu xuất hiện như sau:

Chúng ta sẽ chạy lệnh sau tại đầu ra đễ chờ dữ liệu xuất hiện:

[root@localhost ~]# tail -f my_pipe

Trong đầu vào chúng ta thực hiện nhập:

[root@localhost ~]# echo "Hello my_pipe" > my_pipe
[root@localhost ~]# echo "Hello my_pipe1" > my_pipe
[root@localhost ~]# echo "Hello my_pipe2" > my_pipe
[root@localhost ~]# echo "Hello my_pipe3" > my_pipe
[root@localhost ~]# echo "Hello my_pipe4" > my_pipe

Quay lại kiểm tra đầu ra của dử liệu ta được kết quả như sau:

[root@localhost ~]# tail -f my_pipe
Hello my_pipe
Hello my_pipe1
Hello my_pipe2
Hello my_pipe3
Hello my_pipe4

Trong thực tế nhiều lúc chúng ta cần thực hiện nhiệm vụ di chuyển dữ liệu giữa các tiến trình chúng ta sẽ sử dụng tính năng hay của named pipe này.

2. Giới thiệu về (filter)

Một đường ống có thể chuyển đầu ra tiêu chuẩn của một hoạt động sang đầu vào tiêu chuẩn của một hoạt động khác, nhưng một bộ lọc có thể sửa đổi luồng. Một bộ lọc (filter) trên hệ điều hành Linux là một chương trình có nhiệm vụ lấy đầu vào tiêu chuẩn, thực hiện xử lý và sau đó ghi kết quả vào đầu ra tiêu chuẩn. Linux có một số lượng lớn các bộ lọc.

Một số bộ lọc (filter) thường sử dụng:

TênChức năng
sortSắp xếp dữ liệu.
headIn ra n dòng dữ liệu từ trên xuống.
tailIn ra n dòng dữ liệu từ dưới lên.
wcĐếm ký tự, từ, dòng, byte.
sed`Tìm kiếm và thay thế dữ liệu.
uniqLoại bỏ các dòng trùng nhau.
awkTìm kiếm và xử lý file text.

Một số ví dụ cơ bản về lọc (filter):

👉 Ví dụ 1: Sử dụng lệnh sort để sắp xếp các dòng trong file.txt theo thứ tự a đến z và ngược lại:

[root@localhost ~]# cat test.txt
enable
apple
google
gmail
delay
nice
safari
[root@localhost ~]# sort test.txt
apple
delay
enable
gmail
google
nice
safari
[root@localhost ~]# sort -r test.txt
safari
nice
google
gmail
enable
delay
apple

👉 Ví dụ 2: Dùng lệnh head xem 3 dòng đầu của file test.txt. Chúng ta chạy lệnh sau để thực hiện:

[root@localhost ~]# cat test.txt
enable
apple
google
gmail
delay
nice
safari
[root@localhost ~]# head -n 3 test.txt
enable
apple
google

👉 Ví dụ 3: Dùng lệnh tail xem 3 dòng cuối của file test.txt:

[root@localhost ~]# cat test.txt
enable
apple
google
gmail
delay
nice
safari
[root@localhost ~]# tail -n 3 test.txt
delay
nice
safari

👉 Ví dụ 4: Dùng lệnh wc đếm số dòng trong file test.txt:

[root@localhost ~]# cat test.txt
enable
apple
google
gmail
delay
nice
safari
[root@localhost ~]# wc -l test.txt
7 test.txt

👉 Ví dụ 5: Dùng lệnh wc đếm các ký tự (byte) trong file test.txt. Thực hiện như sau:

[root@localhost ~]# cat test.txt
enable
apple
google
gmail
delay
nice
safari
[root@localhost ~]# wc -c test.txt
44 test.txt

👉 Ví dụ 6: Sử dụng lệnh sed để in và thay thế. Thực hiện như bên dưới:

[root@localhost ~]# cat test.txt
enable
apple
google
gmail
delay
nice
safari
[root@localhost ~]# sed -n 's/a/A/p' test.txt
enAble
Apple
gmAil
delAy
sAfari
[root@localhost ~]# sed -n '/ri/ s/sa/SA/p' test.txt
SAfari

👉 Ví dụ 7: Dùng lệnh uniq để lọc các dòng trùng lặp trong file test.txt. Chúng ta thưc hiện như sau:

[root@test1 ~]# sort file.txt
apple
apple
child
child
delay
delay
hand
hand
nice
nice
rain
rain
safari
safari
[root@test1 ~]# uniq file.txt
apple
child
delay
hand
nice
rain
safari

👉 Ví dụ 8: Dùng lệnh awk in các dòng trong file:

[root@localhost ~]# cat test.txt
enable
apple
google
gmail
delay
nice
safari
[root@localhost ~]# awk '{print}' test.txt
enable
apple
google
gmail
delay
nice
safari

👉 Ví dụ 9: Dùng lệnh awk để lọc các kí tự trong file test.txt. Các kí tự cần lọc được đặt '//'. Thực hiện như sau:

[root@test1 ~]#cat file2.txt
Roses are red,
Violets are blue,
Sugar is sweet,
And so are you.
[root@test1 ~]# awk '/are/' file2.txt
Roses are red,
Violets are blue,
And so are you.
[root@test1 ~]# awk '!/are/' file2.txt
Sugar is sweet,
[root@test1 ~]# awk '/are/ && !/so/' poem.txt
Roses are red,
Violets are blue,
[root@test1 ~]# awk '/^[ab]/' file.txt
apple   42
banana  31
[root@test1 ~]# awk '/are/{print $NF}' file.txt
red,
blue,
you.

3. Giới thiệu về Redirection

Chuyển hướng (redirection) là một tính năng mạnh mẽ trên hệ điều hành Linux để khi thực hiện lệnh chúng ta có thể thay đổi các thiết bị đầu vào/đầu ra tiêu chuẩn. Với tính năng chuyển hướng, các lệnh của chúng ta có thể gửi và nhận luồng dữ liệu đến từ các tệp và thiết bị, cũng như cho phép chúng ta kết nối các chương trình khác với nhau thành các đường ống.

File descriptor (mô tả tập tin) mặc định 0, 1, 2. Bất cứ khi nào chúng ta thực hiện một chương trình/lệnh tại thiết bị đầu cuối, nó sẽ mở ba mô tả tệp (File descriptor) tiêu chuẩn: stdin (mô tả tệp 0), stdout (mô tả tệp 1) và stderr (mô tả tệp 2). Chúng ta có thể mở thêm các mô tả tệp (như 3,4,5... và đóng chúng). Bạn cũng có thể sao chép mô tả tệp.

Ba mô tả tệp (File descriptor) tiêu chuẩn

Bộ mô tả tệp luôn trỏ đến một số tệp (trừ khi chúng bị đóng). Đầu vào được đọc từ những gì bạn nhập vào thiết bị đầu cuối và cả hai đầu ra được gửi đến thiết bị đầu cuối.

Khi bash chạy một lệnh, nó sẽ tạo ra một tiến trình con (chạy lệnh sau: man 2 fork để xem) kế thừa tất cả các mô tả tệp từ tiến trình cha, sau đó nó thiết lập các chuyển hướng mà bạn đã chỉ định và thực thi lệnh (chạy lệnh sau: man 3 exec để xem).

3.1.Chuyển hướng đầu ra

3.1.1. Chuyển hướng đầu ra tiêu chuẩn của lệnh sang tệp

Chuyển hướng đầu ra tiêu chuẩn của lệnh sang tệp

Biểu tượng > được sử dụng chuyển hướng cho đầu ra (STDOUT). Bash sẽ mở tệp để viết và nếu thành công, nó sẽ gửi command đến tệp mới mở. Nếu nó không mở được tệp, toàn bộ command sẽ thất bại.

Lệnh command > file giống như viết command 1>file. Số 1 viết tắt của stdout, là số mô tả tệp cho đầu ra tiêu chuẩn.

Ví dụ:

[root@localhost ~]# ls -la
total 36
dr-xr-x---.  2 root root  200 May 14 14:22 .
dr-xr-xr-x. 17 root root  224 May 14 04:15 ..
-rw-------.  1 root root 1264 May 14 10:40 anaconda-ks.cfg
-rw-------.  1 root root  307 May 14 04:18 .bash_history
-rw-r--r--.  1 root root   18 Dec 28  2013 .bash_logout
-rw-r--r--.  1 root root  176 Dec 28  2013 .bash_profile
-rw-r--r--.  1 root root  176 Dec 28  2013 .bashrc
-rw-r--r--.  1 root root  100 Dec 28  2013 .cshrc
prw-rw----.  1 root root    0 May 14 14:37 my_pipe
-rw-r--r--.  1 root root  218 May 14 14:12 my_pipe1
-rw-r--r--.  1 root root    0 May 14 14:22 pcodein.pl
-rw-r--r--.  1 root root  129 Dec 28  2013 .tcshrc
-rw-r--r--.  1 root root   44 May 14 22:40 test.txt
[root@localhost ~]# ls -la 1> descriptor
[root@localhost ~]# cat descriptor
total 36
dr-xr-x---.  2 root root  218 May 15 00:00 .
dr-xr-xr-x. 17 root root  224 May 14 04:15 ..
-rw-------.  1 root root 1264 May 14 10:40 anaconda-ks.cfg
-rw-------.  1 root root  307 May 14 04:18 .bash_history
-rw-r--r--.  1 root root   18 Dec 28  2013 .bash_logout
-rw-r--r--.  1 root root  176 Dec 28  2013 .bash_profile
-rw-r--r--.  1 root root  176 Dec 28  2013 .bashrc
-rw-r--r--.  1 root root  100 Dec 28  2013 .cshrc
-rw-r--r--.  1 root root    0 May 15 00:00 descriptor
prw-rw----.  1 root root    0 May 14 14:37 my_pipe
-rw-r--r--.  1 root root  218 May 14 14:12 my_pipe1
-rw-r--r--.  1 root root    0 May 14 14:22 pcodein.pl
-rw-r--r--.  1 root root  129 Dec 28  2013 .tcshrc
-rw-r--r--.  1 root root   44 May 14 22:40 test.txt

Qua ví dụ trên thì đầu ra của lệnh ls -la được chuyển hướng đến tập tin descriptor thay vì xuất hiện trên màn hình của bạn.

Lưu ý: Cần sử dụng tên tệp chính xác trong khi chuyển hướng đầu ra lệnh sang tệp. Nếu có một tệp hiện có cùng tên, lệnh được chuyển hướng sẽ xóa nội dung của tệp đó và sau đó nó có thể bị ghi đè.

Nếu bạn không muốn một tập tin bị ghi đè và muốn thêm nhiều nội dung vào một tập tin hiện có, thì bạn nên sử dụng >>.

3.1.2. Chuyển hướng lỗi tiêu chuẩn của lệnh sang tệp

Chuyển hướng lỗi tiêu chuẩn của lệnh sang tệp

Lệnh command 2> file bash chuyển hướng stderr tới tập tin. Số 2 là viết tắt của stderr.

Ví dụ như sau:

[root@localhost ~]# ls -l
total 260
-rw-------.  1 root root   1264 May 14 10:40 anaconda-ks.cfg
-rw-r--r--.  1 root root  87056 May 24 09:27 ddrescue-1.17-1.el7.rf.x86_64.rpm
-rw-r--r--.  1 root root      0 May 25 14:51 descriptor
drwxr-xr-x. 38 root root   4096 May 16 05:13 Desktop
drwxr-xr-x.  2 root root      6 May 16 05:26 Documents
prw-r--r--.  1 root root      0 May 25 13:42 estpipe
-rw-r--r--.  1 root root     18 May 25 09:48 file2.txt
-rw-r--r--.  1 root root 142460 May 24 09:26 index.html
drwx------.  2 root root      6 May 24 06:53 Mail
drwxr-xr-x.  2 root root      6 May 16 05:26 Music
prw-r--r--.  1 root root      0 May 25 14:15 my_pipe
-rw-r--r--.  1 root root     29 May 25 09:41 netowrk.txt
prw-r--r--.  1 root root      0 May 25 13:26 pcodein.pl
prw-r--r--.  1 root root      0 May 25 13:26 pcode.pipe
[root@localhost ~]# ls -l 2> descriptor2
total 260
-rw-------.  1 root root   1264 May 14 10:40 anaconda-ks.cfg
-rw-r--r--.  1 root root  87056 May 24 09:27 ddrescue-1.17-1.el7.rf.x86_64.rpm
-rw-r--r--.  1 root root      0 May 25 14:51 descriptor
-rw-r--r--.  1 root root      0 May 25 14:59 descriptor2
drwxr-xr-x. 38 root root   4096 May 16 05:13 Desktop
drwxr-xr-x.  2 root root      6 May 16 05:26 Documents
prw-r--r--.  1 root root      0 May 25 13:42 estpipe
-rw-r--r--.  1 root root     18 May 25 09:48 file2.txt
-rw-r--r--.  1 root root 142460 May 24 09:26 index.html
drwx------.  2 root root      6 May 24 06:53 Mail
drwxr-xr-x.  2 root root      6 May 16 05:26 Music
prw-r--r--.  1 root root      0 May 25 14:15 my_pipe
-rw-r--r--.  1 root root     29 May 25 09:41 netowrk.txt
prw-r--r--.  1 root root      0 May 25 13:26 pcodein.pl
prw-r--r--.  1 root root      0 May 25 13:26 pcode.pipe

Bash mở file để viết, lấy file descriptor của tệp này và nó thay thế file descriptor 2 bằng file descriptor của tệp này. Vì vậy, bây giờ bất cứ gì được viết cho stderr được ghi vào tập tin.

3.1.3. Chuyển hướng cả đầu ra tiêu chuẩn và lỗi tiêu chuẩn sang một tệp

👉 Ví dụ 1: Trong ví dụ này chúng ta sử dụng cú pháp như sau: command &> file. Trong đó &> dùng chuyển hướng command sau cho cả hai luồng đầu ra stdout và stderr đều đến file. Đây là cách để chúng ta có thể chuyển hướng cả hai luồng đến cùng một đích.

Chuyển hướng cả đầu ra tiêu chuẩn và lỗi tiêu chuẩn sang một tệp 3

Minh hoạt cho ví dụ trên:

[root@localhost ~]# ls -l
total 260
-rw-------.  1 root root   1264 May 14 10:40 anaconda-ks.cfg
-rw-r--r--.  1 root root  87056 May 24 09:27 ddrescue-1.17-1.el7.rf.x86_64.rpm
-rw-r--r--.  1 root root      0 May 25 14:51 descriptor
-rw-r--r--.  1 root root      0 May 25 14:59 descriptor2
-rw-r--r--.  1 root root      0 May 25 15:10 descriptor3
drwxr-xr-x. 38 root root   4096 May 16 05:13 Desktop
drwxr-xr-x.  2 root root      6 May 16 05:26 Documents
prw-r--r--.  1 root root      0 May 25 13:42 estpipe
-rw-r--r--.  1 root root     18 May 25 09:48 file2.txt
-rw-r--r--.  1 root root 142460 May 24 09:26 index.html
drwx------.  2 root root      6 May 24 06:53 Mail
drwxr-xr-x.  2 root root      6 May 16 05:26 Music
prw-r--r--.  1 root root      0 May 25 14:15 my_pipe
-rw-r--r--.  1 root root     29 May 25 09:41 netowrk.txt
prw-r--r--.  1 root root      0 May 25 13:26 pcodein.pl
prw-r--r--.  1 root root      0 May 25 13:26 pcode.pipe
[root@localhost ~]# ls -l &> descriptor3
[root@localhost ~]# cat descriptor3
total 260
-rw-------.  1 root root   1264 May 14 10:40 anaconda-ks.cfg
-rw-r--r--.  1 root root  87056 May 24 09:27 ddrescue-1.17-1.el7.rf.x86_64.rpm
-rw-r--r--.  1 root root      0 May 25 14:51 descriptor
-rw-r--r--.  1 root root      0 May 25 14:59 descriptor2
-rw-r--r--.  1 root root      0 May 25 15:10 descriptor3
drwxr-xr-x. 38 root root   4096 May 16 05:13 Desktop
drwxr-xr-x.  2 root root      6 May 16 05:26 Documents
prw-r--r--.  1 root root      0 May 25 13:42 estpipe
-rw-r--r--.  1 root root     18 May 25 09:48 file2.txt
-rw-r--r--.  1 root root 142460 May 24 09:26 index.html
drwx------.  2 root root      6 May 24 06:53 Mail
drwxr-xr-x.  2 root root      6 May 16 05:26 Music
prw-r--r--.  1 root root      0 May 25 14:15 my_pipe
-rw-r--r--.  1 root root     29 May 25 09:41 netowrk.txt
prw-r--r--.  1 root root      0 May 25 13:26 pcodein.pl
prw-r--r--.  1 root root      0 May 25 13:26 pcode.pipe

Ngoài ra bạn cũng có thể chuyển hướng cả hai luồng đầu ra stdout và stderr đến cùng một đích bằng cách thực hiện chuyển từng luồng như lệnh bên dưới:

command >file 2>&1

Khi bash thấy một lệnh chuyển hướng thì nó sẽ xử lý chúng theo chiều từ trái sang phải. Trước khi thực hiện chạy một lệnh thì bảng mô tả tệp của bash như thế này:

Bảng mô tả tệp của bash khi chưa thực hiện lệnh

Trong lệnh trên thì đầu tiên bash sẽ xử lý chuyển hướng >file. Bảng mô tả tệp có dạng như sau:

Bash xử lý chuyển hướng đầu tiên >file

Tiếp theo bash sẽ chuyển hướng 2>&1. Lệnh này dùng để sao chép bộ mô tả tệp 2 thành bản sao của bộ mô tả tệp 1:

Bash sẽ chuyển hướng 2>&1

Cả hai luồng đầu ra stdout và stderr đều được chuyển hướng đến file.

👉 Ví dụ 2: Lệnh command 2>&1 >file chỉ chuyển hướng đầu ra tiêu chuẩn vào một tệp và stderr sẽ in ra thiết bị đầu cuối.

Chúng ta cần thực hiện các bước của lệnh trên sẽ chạy để hiểu cách hoạt động của chúng. Đầu tiên trước khi chạy một lệnh, bảng mô tả tệp trông như thế này:

Bảng mô tả tệp của bash khi chưa thực hiện lệnh

Tiếp theo bash sẽ thực hiện tiến trình chuyển hướng từ trái sang phải. Nó sẽ thực hiện 2>&1 để sao chép stderr thành stdout.

Bash sẽ chuyển hướng 2>&1

Tiếp theo thì bash sẽ thực hiện chuyển hướng thứ hai >file nó sẽ chuyển hướng stdout đến file như sau:

Bash sẽ chuyển hướng >file

Bây giờ thì stdout trỏ đến file nhưng stderr trỏ đến thiết bị đầu cuối. Mọi thứ ghi vào stderr vẫn được in ra màn hình.

Lưu ý: Khi thực hiện chuyển hướng thì chúng ta cần phải chú ý và cẩn thận với thứ tự chuyển hướng

3.1.4. Mở tệp để đọc bằng mô tả tệp tùy chỉnh

Lệnh exec 3<file sử dụng lệnh exec và chỉ định chuyển hướng đến 3<file. Nó sẽ mở tệp để đọc và gán bộ mô tả tệp đã mở cho số mô tả tệp của shell 3. Bảng mô tả tệp như thế này:

Mở tệp để đọc bằng mô tả tệp tùy chỉnh

Bạn có thể đọc một dòng từ bộ mô tả tập tin 3 bằng lệnh sau:

[root@localhost ~]# grep "foo" <$3

Sau khi sử dụng xong fd3, bạn có thể đóng nó theo cách này:

[root@localhost ~]# exec 3>&-

Ví dụ 1: Mở tệp để viết bằng mô tả tệp tùy chỉnh

Lệnh exec 4>file thì bash sẽ để mở tệp để viết và gán số đó 4. Bảng mô tả tệp như thế này:

Mở tệp để viết bằng mô tả tệp tùy chỉnh

Mô tả tệp không phải được sử dụng theo thứ tự, bạn có thể mở bất kỳ số mô tả tệp nào bạn muốn từ 0 đến 255.

Chúng ta thực hiện viết vào phần mô tả tệp 4:

[root@localhost ~]# echo "File descriptor 4" >&4
[root@localhost ~]# cat < file
File descriptor 4

Để đóng bộ mô tả tệp 4:

[root@localhost ~]# exec 4>&-
[root@localhost ~]# cat < file
-bash: file: No such file or directory

Ví dụ 2: Mở một tệp cả để viết và đọc

Lệnh exec 5<>file sử dụng toán tử <> của bash. Toán tử này sẽ mở một mô tả tập tin cho cả đọc và viết. Thực hiện như sau:

#Viết chuỗi "File descriptor 5" vào file
[root@localhost ~]# echo "File descriptor 5" >file
#Mở file cho rw và gán cho nó fd5
exec 5<> file
#Đọc 4 ký tự đầu tiên từ fd5
[root@localhost ~]# read -n 4 var <&5
[root@localhost ~]# echo $var
File
#Ghi "+" ở vị trí thứ 5
[root@localhost ~]# echo -n + >&5
#Đóng fd5 
[root@localhost ~]# exec 5>&-
[root@localhost ~]# cat file
File+descriptor 3

3.1.5. Truy cập một trang web thông qua bash

Bash xem tệp /dev/tcp/host/port là một tệp đặc biệt và không cần tồn tại trên hệ thống. Tập tin đặc biệt này là để mở các kết nối tcp thông qua bash. Hoặc bạn cũng có thể tạo kết nối upd thông qua /dev/udp/host/port tệp đặc biệt.

Trong ví dụ này, chúng ta mở bộ mô tả tệp 5 có thể đọc, viết và trỏ nó vào tệp đặc biệt /dev/tcp/www.google.com/80 để có thể kết nối đến www.google.com qua cổng 80.

[root@localhost ~]# exec 5<>/dev/tcp/www.google.com/80

Tiếp theo ghi "GET / HTTP/1.1\n\n" vào bộ mô tả tệp 5. Và thực hiện đọc bộ mô tả tệp bằng lệnh cat.

[root@localhost ~]# echo -e "GET / HTTP/1.1\n\n" >&3
[root@localhost ~]# cat <&3
HTTP/1.1 200 OK
Date: Sun, 26 May 2019 05:17:19 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2019-05-26-05; expires=Tue, 25-Jun-2019 05:17:19 GMT; path=/; domain=.google.com
Set-Cookie: NID=184=MCAS1EOzek5Gvxz76Hw44mps49z9I6_ArImWRzZgtVRAi1Xmh3UhyM9SoYxont-vUWsEENJKCOdTewnruWgKUCgndNeyqjg7AM7dQse_0vQB8k4j73GrGGETLtEjDmybTqzEUk1fOszhp00hxGBrtcZ7-gn8ZLtND6LzkmqjtOY; expires=Mon, 25-Nov-2019 05:17:19 GMT; path=/; domain=.google.com; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked
...

3.2. Chuyển hướng đầu vào

Theo mặc định, đầu vào tiêu chuẩn được nhập từ bàn phím, nhưng chúng ta có thể sử dụng biểu tượng < để chấp nhận đầu vào từ các nguồn khác.

Ví dụ: Sử dụng lệnh tr (lệnh tr được sử dụng để xóa hoặc dịch các ký tự). Được thực hiện như sau:

[root@localhost ~]# echo 'Have a goot tay' > output.txt
[root@localhost ~]# cat output.txt
Have a goot tay
[root@localhost ~]# tr < output.txt t d
Have a good day

Ý nghĩa của lệnh tr bên trên là chúng ta đưa vào lệnh tr một chuỗi bằng việc chuyển hướng đầu vào từ một file sử dụng biểu tượng <, và chỉ định ký tự muốn thay đổi sau đó là kí tự sử dụng để thay thế nó.

4. Lời kết

Qua bài trên, giúp cho chúng ta biết các định nghĩa cùng với một số ví dụ cơ bản minh họa về việc sử dụng đường ống (Pipe), lọc (Filter) và chuyển hướng (Redirection) trên hệ điều hành Linux để chúng ta có thể áp dụng vào thực tế một cách tốt nhất.