shell重定向小記

對於int型的,使用open,write,read,close操作,對於FILE*型的使用fopen,fwrite,fread,fclose操作


標準輸入,int fd = 0,int fd = STDIN_FILENO,FILE *f = stdin,shell重定向標準輸入使用"<",比如./hello <words.txt,那麼hello可以從fd=0或者f=stdin裏面讀取words.txt文件中的內容,平時都是./hello words.txt這樣,可以從argv[1]中讀取文件名,然後再open/read/close。


有種特殊的情況是這樣的,不一定需要使用EOF,任意單詞都可以,首尾相同:

$ cat <<EOF
> hello
> world
> EOF
hello
world
$ cat >tmp <<EOF
hello
world
EOF

$ cat tmp
hello
world


有些程序可以接收參數"-",表示從標準輸入中讀取文件內容


標準輸出,int fd = 1,int fd = STDOUT_FILENO,FILE *f = stdout,shell重定向標準輸出使用">"(覆蓋方式),或者">>"(append方式),">"和">>"前面的默認數字是"1",比如:

$ echo hello 1> tmp
$ echo world > tmp
$ cat tmp
world
$ echo hello >>tmp
$ echo world >>tmp
$ cat tmp
world
hello
world


錯誤輸出,int fd = 2,int fd = STDERR_FILENO,FILE *f = stderr,shell重定向錯誤輸出使用"2>"(覆蓋方式),或者"2>>"(append方式),和標準輸出類似,注意使用">"對錯誤輸出無效:

$ cat <<EOF >x.c
> #include <stdio.h>
> int main(void)
> {
>     write(2,"hello world\n",12);
>     return 0;
> }
> EOF
$ gcc -o x x.c
$ ./x >tmp
hello world
$ ./x 2>tmp
$ ./x 2>tmp
$ cat tmp
hello world
$ ./x 2>>tmp
$ cat tmp
hello world
hello world
並不一定是2這個數字,而是指錯誤輸出,例如:

$ cat <<EOF >x.c
> #include <stdio.h>
> #include <unistd.h>
> int main()
> {
>     int fd = dup(2);
>     write(fd,"hello world\n",12);
>     return 0;
> }
> EOF
$ gcc -o x x.c
$ ./x >/dev/null 2>&1
$


有時候常用到"2>&1"這種,是將錯誤輸出重定向到標準輸出,爲什麼要加"&"呢,因爲不加就會重定向到一個名爲"1"的文件

如果標準輸出和錯誤輸出都需要重定向,那麼應該先寫標準輸出的,再寫錯誤輸出的:

$ ./x 2>&1 >/dev/null
hello world
$ ./x >/dev/null 2>&1
$
/dev/null就是個垃圾桶,扔進去的都沒了,類似還有/dev/zero,從裏面可以讀出來無數的0


shell 使用"|"將上一條命令的標準輸出,作爲下一條命令的標準輸入,例如:

$ cat x.c
#include <stdio.h>
int main(void)
{
    write(2,"hello world\n",12);
    return 0;
}
$ cat x.c | grep return
    return 0;


可以使用proc看到每個進程打開了多少fd,以及fd打開的是誰(self可以替換爲具體的pid):

$ cd /proc/self/fd
$ ls -l
total 0
lrwx------ 1 xxxxxx users 64 Mar  7 22:31 0 -> /dev/pts/0
lrwx------ 1 xxxxxx users 64 Mar  7 22:31 1 -> /dev/pts/0
lrwx------ 1 xxxxxx users 64 Mar  7 22:31 2 -> /dev/pts/0
lrwx------ 1 xxxxxx users 64 Mar  7 22:46 255 -> /dev/pts/0


每一個終端都對應/dev/pts下面的一個文件,向對應的文件寫入數據,那麼將會顯示在我們的終端上面,也可以和別的用戶打招呼,如果你sudo的話:

$ cd /dev/pts/
$ ls -l
total 0
crw--w---- 1 xxxxxx  tty  136,  0 Mar  7 23:21 0
crw--w---- 1 root        tty  136, 12 May 27  2015 12
crw--w---- 1 root        tty  136,  9 May 18  2015 9
$ echo hello >0
hello


還有個比較有意思的就是pipe文件,注意ls -l顯示出的文件類型爲"p":

$ mkfifo mypipe
$ ls -l mypipe
prw-r--r-- 1 xxxxxx users 0 Mar  7 23:24 mypipe

可以一個進程將輸出寫到mypipe,另一個進程從mypipe文件讀取第一個進程輸出的內容,


甚至可以這樣玩,先在一個終端上啓動nc服務器:

tree@iZ28dbdyyxuZ:~$ nc -lvnp 8888
Listening on [0.0.0.0] (family 0, port 8888)
Connection from [127.0.0.1] port 8888 [tcp/*] accepted (family 2, sport 55010)
另一個終端使用pipe文件,將bash的標準輸出給nc的標準輸入,nc的標準輸出給bash的標準輸入:

/bin/bash <mypipe | nc localhost 8888 >mypipe

這樣的話,就可以在第一個終端,輸入shell命令了,控制了第二個終端所在的機器


關於time,你沒法重定向time的輸出:

$ time ls
log  mypipe  x  x.c

real    0m0.002s
user    0m0.000s
sys     0m0.000s
$ time ls >/dev/null 2>&1

real    0m0.002s
user    0m0.000s
sys     0m0.000s

因爲time是shell builtin的命令,重定向是由shell自己來處理的,使用time命令的時候,重定向的東西被當做普通參數了。所以time得到的argv={"ls", ">/dev/null", "2>&1"},而不是{"ls"}

如果是普通的程序,比如/usr/bin/time,重定向的東西就是重定向,沒法被當做普通參數,所以/usr/bin/time可以被重定向掉:

$ /usr/bin/time ls
log  mypipe  x  x.c
0.00user 0.00system 0:00.00elapsed 0%CPU (0avgtext+0avgdata 3840maxresident)k
0inputs+0outputs (0major+298minor)pagefaults 0swaps
$ /usr/bin/time ls >/dev/null
0.00user 0.00system 0:00.00elapsed 0%CPU (0avgtext+0avgdata 3840maxresident)k
0inputs+0outputs (0major+298minor)pagefaults 0swaps
$ /usr/bin/time ls >/dev/null 2>&1



發佈了31 篇原創文章 · 獲贊 11 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章