Bài 41.224 phút đọc
dup, dup2, và chuyển hướng
Sau bài này bạn sẽ làm được
- Giải thích dup và dup2 làm gì
- Chuyển hướng stdout và stderr bằng dup2
- Cài lại chuyển hướng ra tệp bằng open cộng dup2
- Không rò rỉ mô tả tệp
Khi bạn gõ cmd > file trong shell, đầu ra của cmd đi vào tệp thay vì màn hình. Cơ chế đằng sau chỉ là hai lời gọi: dup2 và close. Bài này chạy được trên MinGW, nên các bản ghi là thật.
#dup và dup2
Chữ ký
#include <unistd.h>
int dup(int oldfd);
/* nhan ban oldfd, tra ve fd MOI NHO NHAT con trong,
ca hai fd tro toi CUNG mot tep */
int dup2(int oldfd, int newfd);
/* nhan ban oldfd vao DUNG newfd (dong newfd truoc neu dang mo),
ca hai tro toi cung mot tep */Nhân bản mô tả tệp
dup và dup2 tạo một mô tả tệp thứ hai trỏ tới cùng một tệp mở như cái đầu. Ghi qua cái nào cũng vào cùng tệp, và chúng chia sẻ cùng vị trí đọc ghi. Đóng một cái không ảnh hưởng cái kia.#Chuyển hướng ra tệp
cmd > file, bằng open + dup2
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, STDOUT_FILENO); /* moi ghi vao stdout gio vao out.txt */
close(fd); /* khong can fd goc nua, stdout da tro dung cho */
printf("Dong nay vao file\n"); /* printf ghi vao stdout = out.txt */
return 0;
}#stdout và stderr riêng
redir.c, tách hai luồng ra hai tệp
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
int out = open("ra.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
int err = open("loi.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(out, STDOUT_FILENO); /* stdout -> ra.txt */
dup2(err, STDERR_FILENO); /* stderr -> loi.txt */
close(out);
close(err);
fprintf(stdout, "dong nay vao ra.txt\n");
fprintf(stderr, "dong nay vao loi.txt\n");
fflush(NULL);
return 0;
}terminal
gcc -O2 -Wall -Wextra redir.c -o redir.exe && ./redir.exe
cat ra.txt
dong nay vao ra.txt
cat loi.txt
dong nay vao loi.txt
#Cơ chế của shell
Chuyển hướng trong con, sau fork trước exec
/* Shell chay "ls > out.txt" nhu the nay: */
pid_t pid = fork();
if (pid == 0) {
/* CON: thiet lap chuyen huong TRUOC khi exec */
int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, STDOUT_FILENO); /* stdout cua con -> out.txt */
close(fd);
execlp("ls", "ls", NULL); /* ls in ra stdout = out.txt */
_exit(127);
}
waitpid(pid, NULL, 0);
/* Diem then chot: chuyen huong xay ra trong CON, GIUA fork va exec.
- Con thua huong bang fd cua cha (nho fork).
- Con doi stdout cua RIENG no sang tep.
- Con exec thanh ls; ls in ra stdout, nhung stdout gio la tep.
- CHA khong bi anh huong: stdout cua cha van la terminal.
Do la ly do Unix tach fork va exec: khoang GIUA hai buoc do
la cho shell thiet lap chuyen huong, pipe, nhom tien trinh.
Xem Bai 43.2 va du an Mini Shell (60.4). */Chuyển hướng trong cha
/* Doi stdout cua CHA truoc khi fork */
dup2(fd, STDOUT_FILENO);
fork();
/* Bay gio CA cha va con deu co stdout tro toi tep. Shell mat
duong ra terminal, moi lenh sau deu ghi vao tep. Hong. */
Chuyển hướng trong con
/* Doi stdout chi trong CON */
if (fork() == 0) {
dup2(fd, STDOUT_FILENO); /* chi con bi anh huong */
execlp(...);
_exit(127);
}
/* Cha giu nguyen stdout, tiep tuc nhac lenh binh thuong. */
Tự làm thử
- Chuyển hướng
stdoutra một tệp bằngdup2và xác nhậnprintfvào tệp. - Chuyển hướng
stdoutvàstderrra hai tệp khác nhau. - Cài
cmd > out.txt 2>&1và giải thích thứ tựdup2. - Viết chương trình
forkcộng chuyển hướng cộngexecđể chạylsghi vào tệp. - Chứng minh chuyển hướng trong cha làm hỏng shell còn trong con thì không.
- Dùng
lseekđể viếttail -n Nđọc N dòng cuối của một tệp.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
dupvàdup2tạo mô tả tệp thứ hai trỏ tới cùng tệp mở;dup2cho chọn số đích.- Chuyển hướng ra tệp là ba bước:
open,dup2vàoSTDOUT_FILENO,closefd gốc. 2>&1làdup2sao chép đích hiện tại củastdoutsangstderr; thứ tự quan trọng.- Shell chuyển hướng trong con, giữa
forkvàexec, để không ảnh hưởng chính shell. lseekdi chuyển vị trí đọc ghi; không dùng được với ống, socket, terminal.