Khi chúng ta gõ lệnh vào terminal, chúng đều trả về một output. Ví dụ:
$ echo hello
hello
Standard output
Nếu bạn nghĩ lệnh echo hello
dùng để xuất chữ hello
ra màn hình terminal thì bạn chỉ đúng một phần thôi. Mỗi hệ điều hành dựa trên nền tàng Unix có một khái niệm là “a default place for output to go” - hay còn gọi lại Standard Output (hay stdout). Là một nơi để chứa những output dùng để hiển thị lên màn hình cho bạn được thấy.
Standard input
Standard input (hay stdin) là một nơi mặc định mà commands sẽ lắng nghe để nhận tham số input. Ví dụ, nếu bạn gõ cat
mà không có tham số, nó sẽ lắng nghe input trong stdin và xuất ra những gì bạn đã gõ vào đó, cho đến khi gặp ký tự EOF (CTRL+D).
Bất kì lệnh nào trong Unix đều có stdout và stdin.
Để không trả về output nào, bạn có thể redirect output đó về một tập tin đặc biệt là > dev/null
. dev/null
sẽ nuốt chửng mọi thứ truyền vào cho nó và không làm gì cả.
Pipes
Pipes làm trung gian và kết nối standard output của một lệnh thành standard input của một lệnh khác. Ví dụ:
$echo "hello there"
hello there
$echo "hello there" | sed "s/hello/hi"
hi there
Ở đây, standard output “hello there” của lệnh echo đã trở thành đầu vào của lệnh sed và sed đã thay chữ hello
bằng chữ hi
và in kết quả vào stdout (rồi hiển thị lên màn hình).
$ echo "hello there" | sed "s/hello/hi/" | sed "s/there/robots/"
hi robots
Standard error
Standard Error (hay stderr) cũng giống như standard output và standard input, nó là nơi mặc định để các thông điệp lỗi đi đến. Để xem một số stderr output, bạn thử catting một file không tồn tại xem.
$cat does-not-exist
cat: does-not-exist: No such file or directory
stderr khá giống stdout nhưng nếu nó vô hiệu khi bạn muốn dùng stderr với Pipes
$ cat does-not-exist | sed 's/No such/ROBOT SMASH/'
cat: does-not-exist: No such file or directory
Bởi vì Pipes lấy stdout chứ không phải stderr. Vậy ngoài cách sử dụng Pipes, còn cách nào để redirect output.
Redirecting output
Mặc định, stdout và stderr đều được in lên màn hình terminal, đó là lý do vì sao mà chúng ta có thể thấy được chúng. Nhưng chúng ta có thể redirect output vào một file bằng cách sử dụng dấu >
$ echo hello
hello
$ echo hello > new-file
$ cat new-file
hello
Ở đây lệnh > new-file
thực hiện 2 công việc cùng lúc:
- Tạo tập tin new-file nếu tập tin này chưa tồn tại.
- Thay thế nội dung của tập tin new-file bằng output của lệnh
echo hello
Thay vì ghi đè, bạn có thể ghi nối (append) bằng cách dùng dấu >>
$ cat new-file
hello
$ echo hello again >> new-file
$ cat new-file
hello
hello again
File descriptors
File descriptors là một biến số nguyên dương tham chiếu đến một nguồn input/output cụ thể: 0 là stdin, 1 là stdout, 2 là stderr. Xem thêm tại đây. Sử dụng dấu >&
để redirect input/output bằng File descriptors.
# Redirect stdout to stdout (FD 1)
$ echo "hello there" >&1
hello there
# Redirect stdout to stderr (FD 2)
$ echo "hello there" >&2
hello there
Ứng dụng của File Descriptors:
# Redirect to stdout, so it comes through the pipe
$ echo "no changes" >&1 | sed "s/no/some/"
some changes
# Redirect to stderr, so it does not come through
$ echo "no changes" >&2 | sed "s/no/some/"
no changes
Advanced file descriptors
Giả sử chúng ta muốn thêm Robot says
vào nội dung của các file - file1, file2, file3 nhưng do file2 là stderr, nó không thể được nhận bởi lệnh sed
nên chúng ta phải redirect stderr
thành stdout
bằng File descriptors, cụ thể cách làm như sau - chuyển 2 (stderr) về 1 (stdout) bằng 2>&1
:
$ ./command file1 file2 file3 2>&1 | sed "s/std/Robot says: std/"
Robot says: stderr file2
Robot says: stdout file1
Robot says: stdout file3
Ứng dụng thực tế
Chuyển tất cả stdout và stderr vào một log-file.
$ ./command file1 file2 file3 > log-file 2>&1
$ cat log-file
stderr file2
stdout file1
stdout file3
Bạn cần chú ý về thứ tự của lệnh trên sẽ dẫn đến kết quả hoàn toàn khác, thứ tự đúng phải là
> log-file 2>&1
chứ không phải là
2>&1 > log-file
Vì lệnh này sẽ redirect stderr về stdout (vốn được in ra màn hình) và stdout được redirect đến log-file
, nhưng stderr sẽ không đi theo vì stderr được chỉ đến stdout “cũ”.
# Redirect stdout, because it's plain `>`
$ ./command file1 file2 file3 > log-file
stderr file2
# Redirect stderr, because it's `2>`
$ ./command file1 file2 file3 2> log-file
stdout file1
stdout file3
Nguồn bài viết: https://robots.thoughtbot.com/input-output-redirection-in-the-shell.