Linux Shell 重定向与管道【转帖】

by 程默html

在了解重定向以前,咱们先来看看linux 的文件描述符。python

linux文件描述符:能够理解为linux跟踪打开文件,而分配的一个数字,这个数字有点相似c语言操做文件时候的句柄,经过句柄就能够实现文件的读写操做。 用户能够自定义文件描述符范围是:3-num,这个最大数字,跟用户的:ulimit –n 定义数字有关系,不能超过最大值。linux

 

linux启动后,会默认打开3个文件描述符,分别是:标准输入standard input 0,正确输出standard output 1,错误输出:error output 2shell

之后打开文件后。新增文件绑定描述符 能够依次增长。 一条shell命令执行,都会继承父进程的文件描述符。所以,全部运行的shell命令,都会有默认3个文件描述符。centos

 

对于任何一条linux 命令执行,它会是这样一个过程:bash

image

一个命令执行了:less

先有一个输入:输入能够从键盘,也能够从文件获得post

命令执行完成:成功了,会把成功结果输出到屏幕:standard output默认是屏幕学习

命令执行有错误:会把错误也输出到屏幕上面:standard error默认也是指的屏幕测试

 

文件输入输出由追踪为一个给定的进程全部打开文件的整数句柄来完成。这些数字值就是文件描述符。最为人们所知的文件米描述符是 stdinstdout 和 stderr,文件描述符的数字分别是0,1和2。这些数字和各自的设备是保留的。一个命令执行前,先会准备好全部输入输出,默认分别绑定(stdin,stdout,stderr),若是这个时候出现错误,命令将终止,不会执行。命令解析过程,能够参考:Linux Shell 通配符、元字符、转义符使用实例介绍

 

这些默认的输出,输入都是linux系统内定的,咱们在使用过程当中,有时候并不但愿执行结果输出到屏幕。我想输出到文件或其它设备。这个时候咱们就须要进行输出重定向了。

 

linux shell下经常使用输入输出操做符是:

1.  标准输入   (stdin) :代码为 0 ,使用 < 或 << ; /dev/stdin -> /proc/self/fd/0   0表明:/dev/stdin 
2.  标准输出   (stdout):代码为 1 ,使用 > 或 >> ; /dev/stdout -> /proc/self/fd/1  1表明:/dev/stdout
3.  标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ; /dev/stderr -> /proc/self/fd/2 2表明:/dev/stderr

 

  • 输出重定向:

格式:

command-line1 [1-n] > file或文件操做符或设备

上面命令意思是:将一条命令执行结果(标准输出,或者错误输出,原本都要打印到屏幕上面的)  重定向其它输出设备(文件,打开文件操做符,或打印机等等)1,2分别是标准输出,错误输出。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#显示当前目录文件 test.sh test1.sh test1.sh实际不存在
[chengmo@centos5 shell]$ ls  test .sh test1.sh
ls : test1.sh: 没有这个文件和目录
test .sh
 
#正确输出与错误输出都显示在屏幕了,如今须要把正确输出写入suc.txt
# 1>能够省略,不写,默认所至标准输出
[chengmo@centos5 shell]$ ls  test .sh test1.sh 1>suc.txt
ls : test1.sh: 没有这个文件和目录
[chengmo@centos5 shell]$ cat  suc.txt
test .sh
 
#把错误输出,不输出到屏幕,输出到err.txt
[chengmo@centos5 shell]$ ls  test .sh test1.sh 1>suc.txt 2>err.txt
[chengmo@centos5 shell]$ cat  suc.txt err.txt
test .sh
ls : test1.sh: 没有这个文件和目录
#继续追加把输出写入suc.txt err.txt  “>>”追加操做符
[chengmo@centos5 shell]$ ls  test .sh test1.sh 1>>suc.txt 2>>err.txt
 
#将错误输出信息关闭掉
[chengmo@centos5 shell]$ ls  test .sh test1.sh 2>&-
test .sh
[chengmo@centos5 shell]$ ls  test .sh test1.sh 2> /dev/null
test .sh
#&[n] 表明是已经存在的文件描述符,&1 表明输出 &2表明错误输出 &-表明关闭与它绑定的描述符
#/dev/null 这个设备,是linux 中黑洞设备,什么信息只要输出给这个设备,都会给吃掉
 
#关闭全部输出
[chengmo@centos5 shell]$ ls  test .sh test1.sh  1>&- 2>&-
#关闭 1 ,2 文件描述符
[chengmo@centos5 shell]$ ls  test .sh test1.sh  2> /dev/null  1> /dev/null
#将1,2 输出转发给/dev/null设备
[chengmo@centos5 shell]$ ls  test .sh test1.sh > /dev/null  2>&1
#将错误输出2 绑定给 正确输出 1,而后将 正确输出 发送给 /dev/null设备  这种经常使用
<p>[chengmo@centos5 shell]$ ls  test .sh test1.sh &> /dev/null
#& 表明标准输出 ,错误输出 将全部标准输出与错误输出 输入到/dev/null文件
< /p >

 

 

注意:

一、shell遇到”>”操做符,会判断右边文件是否存在,若是存在就先删除,而且建立新文件。不存在直接建立。 不管左边命令执行是否成功。右边文件都会变为空。

二、“>>”操做符,判断右边文件,若是不存在,先建立。以添加方式打开文件,会分配一个文件描述符[不特别指定,默认为1,2]而后,与左边的标准输出(1)或错误输出(2) 绑定。

三、当命令:执行完,绑定文件的描述符也自动失效。0,1,2又会空闲。

四、一条命令启动,命令的输入,正确输出,错误输出,默认分别绑定0,1,2文件描述符。

五、一条命令在执行前,先会检查输出是否正确,若是输出设备错误,将不会进行命令执行

  • 输入重定向

格式:

command-line [n] <file或文件描述符&设备

将然有,命令默认从键盘得到的输入,改为从文件,或者其它打开文件以及设备输入。执行这个命令,将标准输入0,与文件或设备绑定。将由它进行输入。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[chengmo@centos5 shell] # cat > catfile
testing
cat  file  test
#这里按下 [ctrl]+d 离开
#从标准输入【键盘】得到数据,而后输出给catfile文件
 
[chengmo@centos5 shell]$ cat >catfile < test .sh
#cat 从test.sh 得到输入数据,而后输出给文件catfile
 
 
[chengmo@centos5 shell]$ cat >catfile <<eof
test  a file
test !
eof
 
#<< 这个连续两个小符号, 他表明的是『结束的输入字符』的意思。这样当空行输入eof字符,输入自动结束,不用ctrl+D

 

 

  • exec绑定重定向

格式:

exec 文件描述符[n] <或> file或文件描述符或设备

在上面讲的输入,输出重定向 将输入,输出绑定文件或设备后。只对当前那条指令是有效的。若是须要在绑定以后,接下来的全部命令都支持的话。就须要用exec命令

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[chengmo@centos5 shell]$ exec  6>&1
#将标准输出与fd 6绑定
 
[chengmo@centos5 shell]$ ls   /proc/self/fd/ 
0  1  2  3  6
#出现文件描述符6
 
[chengmo@centos5 shell]$ exec  1>suc.txt
#将接下来全部命令标准输出,绑定到suc.txt文件(输出到该文件)
 
[chengmo@centos5 shell]$ ls  -al
#执行命令,发现什么都不返回了,由于标准输出已经输出到suc.txt文件了
 
[chengmo@centos5 shell]$ exec  1>&6
#恢复标准输出
 
 
[chengmo@centos5 shell]$ exec  6>&-
#关闭fd 6描述符
 
[chengmo@centos5 ~]$ ls  /proc/self/fd/
0  1  2  3

说明:使用前先将标准输入保存到文件描述符6,这里说明下,文件描述符默认会打开0,1,2 还可使用自定义描述符 。而后对标准输出绑定到文件,接下来全部输出都会发生到文件。 使用完后,恢复标准的输出,关闭打开文件描述符6。

有趣事情:

可能有朋友会这样用:exec 1>suc.txt ,接下来全部输出都绑定到suc.txt 文件,那么怎么样恢复原来的呢? 试试你就会发现问题所在……

  • 复杂一点实例
1
2
3
4
5
6
7
8
9
10
11
exec  3<> test .sh;
#打开test.sh可读写操做,与文件描述符3绑定
 
while  read  line<&3
  do
     echo  $line;
done
#循环读取文件描述符3(读取的是test.sh内容)
exec  3>&-
exec  3<&-
#关闭文件的,输入,输出绑定

 

  • 总结下:

学习就要总结,总结才能够提升了。哈哈!

估计还有一些朋友是头晕晕的。怎么linux的重定向这么复杂呢,又是文件打开描述符又是读,还有些,还有默认标准输入输出。

其实,总结一下,重定向应用一般就如下两点:

一、从新设置命令的默认输入,输出,指向到本身文件(文件,文件描述符,设备其实都是文件,由于linux就是基于设备也是文件,描述符也指向是文件,哈哈)

二、扩展本身新的描述符,对文件进行读写操做

 
[Mid]

此次咱们看下管道命令了。shell管道,能够说用法就简单多了。

 

管道命令操做符是:”|”,它仅能处理经由前面一个指令传出的正确输出信息,也就是 standard output 的信息,对于 stdandard 
error 信息没有直接处理能力。而后,传递给下一个命令,做为标准的输入 standard input.

 

  • 管道命令使用说明:

先看下下面图:

image

command1正确输出,做为command2的输入 而后comand2的输出做为,comand3的输入 ,comand3输出就会直接显示在屏幕上面了。

经过管道以后:comand1,comand2的正确输出不显示在屏幕上面

注意:

一、管道命令只处理前一个命令正确输出,不处理错误输出

二、管道命令右边命令,必须可以接收标准输入流命令才行。

实例:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[chengmo@centos5 shell]$ cat  test .sh | grep  -n 'echo'
5:    echo  "very good!" ;
7:    echo  "good!" ;
9:    echo  "pass!" ;
11:    echo  "no pass!" ;
#读出test.sh文件内容,经过管道转发给grep 做为输入内容
 
[chengmo@centos5 shell]$ cat  test .sh test1.sh | grep  -n 'echo'
cat : test1.sh: 没有那个文件或目录
5:    echo  "very good!" ;
7:    echo  "good!" ;
9:    echo  "pass!" ;
11:    echo  "no pass!" ;
#cat test1.sh不存在,错误输出打印到屏幕,正确输出经过管道发送给grep
 
 
[chengmo@centos5 shell]$ cat  test .sh test1.sh 2> /dev/null  | grep  -n 'echo' 
5:    echo  "very good!" ;
7:    echo  "good!" ;
9:    echo  "pass!" ;
11:    echo  "no pass!" ;
#将test1.sh 没有找到错误输出重定向输出给/dev/null 文件,正确输出经过管道发送给grep
 
 
[chengmo@centos5 shell]$ cat  test .sh | ls
catfile      httprequest.txt  secure  test             testfdread.sh  testpipe.sh    testsh.sh      testwhile2.sh
envcron.txt  python           sh      testcase.sh     testfor2.sh    testselect.sh  test .txt       text.txt
env .txt      release          sms     testcronenv.sh  testfor.sh     test .sh        testwhile1.sh
#读取test.sh内容,经过管道发送给ls命令,因为ls 不支持标准输入,所以数据被丢弃

 

这里实例就是对上面2点注意的验证。做用接收标准输入的命令才能够用做管道右边。不然传递过程当中数据会抛弃。 经常使用来做为接收数据管道命令有:sed,awk,cut,head,top,less,more,wc,join,sort,split 等等,都是些文本处理命令。

  • 管道命令与重定向区别

区别是:

一、左边的命令应该有标准输出 | 右边的命令应该接受标准输入
   左边的命令应该有标准输出 > 右边只能是文件
   左边的命令应该须要标准输入 < 右边只能是文件

 

二、管道触发两个子进程执行"|"两边的程序;而定向是在一个进程内执行

这些都是网上总结不少的,其实只要多加清楚用法,也必定有本身的一份不一样描述。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#能够相互转换状况
#输入重定向
 
[chengmo@centos5 shell]$ cat  test .sh| grep  -n 'echo'
5:    echo  "very good!" ;
7:    echo  "good!" ;
9:    echo  "pass!" ;
11:    echo  "no pass!" ;
#"|"管道两边都必须是shell命令
 
 
[chengmo@centos5 shell]$ grep  -n 'echo'  < test .sh   
5:    echo  "very good!" ;
7:    echo  "good!" ;
9:    echo  "pass!" ;
11:    echo  "no pass!" ;
#"重定向"符号,右边只能是文件(普通文件,文件描述符,文件设备)
 
 
[chengmo@centos5 shell]$ mail -s 'test'  8292669@qq.com < test .sh
[chengmo@centos5 shell]$ cat  test .sh|mail -s 'test'  8292669@qq.com
#以上2个也相同,将test.sh内容发送到指定邮箱。
 
 
[chengmo@centos5 shell]$ ( sed  -n '1,$p' | grep  -n 'echo' )< test .sh
5:    echo  "very good!" ;
7:    echo  "good!" ;
9:    echo  "pass!" ;
11:    echo  "no pass!" ;
#这个脚本比较有意思了。因为前面是管道,后面须要把test.sh内容重定向到 sed ,而后sed输出经过管道,输入给grep.须要将前面用"()"运算符括起来。在单括号内的命令,能够把它们看做一个象一个命令样。若是不加括号test.sh就是grep 的输入了。
 
 
#上面一个等同于这个
[chengmo@centos5 shell]$ sed  -n '1,$p' < test .sh | grep  -n 'echo'
5:    echo  "very good!" ;
7:    echo  "good!" ;
9:    echo  "pass!" ;
11:    echo  "no pass!" ;
 
#重定向运算符,在shell命令解析前,首先检查的(一个命令,执行前必定检查好它的输入,输出,也就是0,1,2 设备是否准备好),因此优先级会最高
 
 
[chengmo@centos5 shell]$ sed  -n '1,10p' < test .sh | grep  -n 'echo'  <testsh.sh
10: echo  $total;
18: echo  $total;
21:     echo  "ok" ;
#哈哈,这个grep又接受管道输入,又有testsh.sh输入,那是否是2个都接收呢。刚才说了"<"运算符会优先,管道尚未发送数据前,grep绑定了testsh.sh输入,这样sed命令输出就被抛弃了。这里必定要当心使用
 
#输出重定向
 
[chengmo@centos5 shell]$ cat  test .sh> test .txt
[chengmo@centos5 shell] cat  test .sh| tee  test .txt &> /dev/null
#经过管道实现将结果存入文件,还须要借助命令tee,它会把管道过来标准输入写入文件test.txt ,而后将标准输入复制到标准输出(stdout),因此重定向到/dev/null 不显示输出
#">"输出重定向,每每在命令最右边,接收左边命令的,输出结果,重定向到指定文件。也能够用到命令中间。
 
 
[chengmo@centos5 shell]$ ls  test .sh test1.sh testsh.sh 2>err.txt | grep  'test'
test .sh
testsh.sh
#目录下面有:test,testsh文件,test1.sh不存在,所以将ls 命令错误输出输入到err.txt 正确输出,还会经过管道发送到grep命令。
[chengmo@centos5 shell]$ ls  test .sh test1.sh testsh.sh &>err.txt | grep  'test'
#此次打印结果是空,&表明正确与错误输出 都输入给err.txt,经过管道继续往下面传递数据为空,因此没有什么显示的
 
#一样">"输出重定向符,优先级也是先解析,当一个命令有这个字符,它就会与左边命令标准输出绑定。准备好了这些,就等待命令执行输出数据,它就开始接收

 

再归纳下:

从上面例子能够看,重定向与管道在使用时候不少时候能够通用,其实,在shell里面,常常是【条条大路通罗马】的。通常若是是命令间传递参数,仍是管道的好,若是处理输出结果须要重定向到文件,仍是用重定向输出比较好。

命令执行顺序能够看下:Linux Shell 通配符、元字符、转义符使用实例介绍

 

 

  • shell脚本接收管道输入

有意思的问题:

既然做用管道接收命令,须要能够接收标准的输入,那么咱们shell脚本是否能够开发出这样的基本程序呢?(你们常常看到的,都是一些系统的命令做为管道接收方)

实例(testpipe.sh):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/sh
  
  if  [ $ # -gt 0 ];then
      exec  0<$1;
#判断是否传入参数:文件名,若是传入,将该文件绑定到标准输入
  fi
  
  while  read  line
  do
      echo  $line;
  done <&0;
#经过标准输入循环读取内容
  exec  0&-;
#解除标准输入绑定

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[chengmo@centos5 shell]$ cat  testpipe.txt
1,t,est pipe
2,t,est pipe
3,t,est pipe
4,t,est pipe
#testpipe.txt 只是须要读取的测试文本
 
[chengmo@centos5 shell]$ cat  testpipe.txt | sh testpipe.sh
1,t,est pipe
2,t,est pipe
3,t,est pipe
4,t,est pipe
#经过cat 读取 testpipe.txt 发送给testpipe.sh 标准输入
 
[chengmo@centos5 shell]$ sh testpipe.sh testpipe.txt     
1,t,est pipe
2,t,est pipe
3,t,est pipe
4,t,est pipe
#testpipe.sh 经过出入文件名读取文件内容
 
[End]
相关文章
相关标签/搜索