哪些命令支持輸入流
cat, more, less, head, tail, cut, sort, wc, sed …
哪些命令不支持輸入流
ls, pwd, cd …
什么是輸入流
輸入流就是標準輸入,在 C 程式里習慣記為 STDIN_FILENO,
/* Standard file descriptors. */
#define STDIN_FILENO 0 /* Standard input. */
#define STDOUT_FILENO 1 /* Standard output. */
#define STDERR_FILENO 2 /* Standard error output. */
疑問
命令還有這區別?平時沒怎么注意過,什么叫做支持輸入流,什么叫做不支持輸入流呢?答案就是這個命令能不能夠從標準輸入讀取資料,能就是支持輸入流,不能就是不支持輸入流,
例如:
支持輸入流:cat
$ cat
hello
hello
我們在命令列輸入 cat 回車,cat 行程就開始從標準輸入讀取資料,讀不到就阻塞,我們從標準輸入(這里是鍵盤設備)輸入 hello 回車,cat 行程就從標準輸入讀取 hello 并輸入到標準輸出上(這里是螢屏設備),
相反,
不支持輸入流:ls
$ ls
bin dev lib libx32 mnt root snap sys var
boot etc lib32 lost+found opt run srv tmp
cdrom home lib64 media proc sbin swapfile usr
我們輸入 ls 回車,它不會企圖從標準輸入讀入內容,就算我們嘗試給它一個輸入,如:
$ echo "hello" | ls
bin dev lib libx32 mnt root snap sys var
boot etc lib32 lost+found opt run srv tmp
cdrom home lib64 media proc sbin swapfile usr
ls 也是非常高冷地忽視,不接受標準輸入的內容,
背后的原理
cat, ls 差異背后的原理是什么呢?最好的探索辦法就是看原始碼
cat.c 部分代碼
FILE *bb_wfopen_input(const char *filename)
{
FILE *fp = stdin;
if ((filename != bb_msg_standard_input)
&& filename[0] && ((filename[0] != '-') || filename[1])
) {
#if 0
/* This check shouldn't be necessary for linux, but is left
* here disabled just in case. */
struct stat stat_buf;
if (is_directory(filename, 1, &stat_buf)) {
bb_error_msg("%s: Is a directory", filename);
return NULL;
}
#endif
fp = bb_wfopen(filename, "r");
}
return fp;
}
可以看到,cat 先判斷有沒有輸入檔案名,輸入了就打開此檔案,沒輸入檔案名就默認打開標準輸入,這就是為什么我們在命令列輸入 cat 直接回車,cat 會從標準輸入讀取內容的原因了,
再來看 ls.c
extern int ls_main(int argc, char **argv)
{
ac = argc - optind; /* how many cmd line args are left */
if (ac < 1) {
av = (char **) xcalloc((size_t) 1, (size_t) (sizeof(char *)));
av[0] = bb_xstrdup(".");
ac = 1;
} else {
av = (char **) xcalloc((size_t) ac, (size_t) (sizeof(char *)));
for (oi = 0; oi < ac; oi++) {
av[oi] = argv[optind++]; /* copy pointer to real cmd line arg */
}
}
/* now, everything is in the av array */
if (ac > 1)
all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
/* stuff the command line file names into an dnode array */
dn = NULL;
for (oi = 0; oi < ac; oi++) {
char *fullname = bb_xstrdup(av[oi]);
cur = my_stat(fullname, fullname);
if (!cur)
continue;
cur->next = dn;
dn = cur;
nfiles++;
}
...
}
ls 只處理了命令列引數,并沒有讀取標準輸入,
小有結論
至此,命令支不支持輸入流的問題已小有結論,就看它有沒有去處理 stdin,
波瀾再生
又有個疑問冒出來了:行程的 標準輸入、標準輸出、標準錯誤 哪來的?
write(1, "test\n", sizeof("test\n"));
為什么這行代碼就能向標準輸出列印 test ?標準輸出 “1” 來自于哪里?
找到答案
按慣例,每當運行一個新程式時,所有的 shell 都為其打開三個檔案描述符:標準輸入(standard input)、標準輸出(standard output) 以及標準錯誤(standard error),如果像簡單命令 ls 那樣沒有做什么特殊處理,則這三個描述符都鏈向終端,
—— 《UNIX 環境高級編程 第3版》
所以說,行程的 標準輸入、標準輸出、標準錯誤 來自于 shell,
繼續追問
那 shell 是怎么把 標準輸入、標準輸出、標準錯誤 給到行程的呢?shell 自身的 標準輸入、標準輸出、標準錯誤 又是源自于哪里呢?
shell 的實施原理
回答上面兩個問題前,我們先了解以下 shell 的基本實施,請參考 《35 行代碼實作一個簡單的 shell》
對于問題一,我的理解:
子行程(我們在 shell 中運行的用戶程式)是被 shell fork() + execlp() 出來的,在 fork 程序中,子行程獲得了和父行程一模一樣的資源,其中就包括 標準輸入、標準輸出、標準錯誤 ,而 execlp 只是替換行程,行程所處的環境沒有變,所以 execlp 替換的新行程依然享有被替換的程式所擁有的 標準輸入、標準輸出、標準錯誤(經過實驗驗證了:父行程中關掉標準輸出,子行程也找不到標準輸出了,但是沒找到理論依據),
對于問題二,我的理解:
shell 行程是被 1 號祖先行程 init 克隆出來的,自然能夠從 init 行程獲取 標準輸入、標準輸出、標準錯誤;而 init 行程又是從 kernel 中獲取到它們的,
以下就是我目前的理解框圖:

思路清晰度
0.7
歡迎大家討論并指正
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286723.html
標籤:其他
上一篇:Python3在Windows、Linux、Mac三大平臺的安裝教程
下一篇:Linux網路配置和常用命令
