wang_rx 发表于 2018-8-29 09:34:09

Linux shell管道与重定向及实例分析

  一、管道
  管道命令操作符是:"|",它仅能处理由前面一个命令的正确输出信息,也就是standard output的信息,对于stdandard error信息没有直接处理能力;然后,传递给下一个命令,作为标准输入standard input。
  linux管道执行过程:

  管道命令使用说明:
  command1正确输出,作为command2的输入,然后command2的输出作为command3的输入,command3的输出打印到屏幕上;
  通过管道:command1、command2的正确输出不会显示在屏幕上。
  注意:
  1、管道命令只处理前一个命令的正确输出,不处理错误输出;
  2、管道命令右边的命令,必须能够接收标准输入流命令。
  实例:
  例1:
  cat test.sh | grep -n 'echo'
  3:   echo "nice";
  5:   echo "good";
  7:   echo "pass";
  9:   echo "shit";
  # 读取test.sh文件内容,通过管道转发给grep作为输入内容
  例2:
  cat test.sh test1.sh | grep -n 'echo'
  cat: test1.sh: 没有那个文件或目录
  3:   echo "nice";
  5:   echo "good";
  7:   echo "pass";
  9:   echo "shit";
  # test1.sh不存在,错误输出打印到屏幕,正确输出通过管道发送给grep
  例3:
  cat test.sh test1.sh 2>/dev/null | grep -n 'echo'
  3:   echo "nice";
  5:   echo "good";
  7:   echo "pass";
  9:   echo "shit";
  # 将错误输出重定向输出给/dev/null文件,正确输出通过管道发送给grep
  例4:
  cat test.sh | ls
  test.sh test1.sh test2.sh
  # 读取test.sh内容,通过管道发送给ls命令,由于ls不支持标准输入,因此数据被丢弃
  二、重定向
  linux文件描述符:
  可以理解为linux跟踪打开文件,而分配的一个数字,这个数字有点类似C语言操作文件时候的句柄,通过句柄就可以实现文件的读写操作;用户可以自定义文件描述符范围:3-num,这个最大数字,跟用户的ulimit -n定义数字有关系,不能超过最大值。
  linux启动后,会默认打开3个文件描述符,分别是:
  标准输入:standard input 0;
  正确输出:standard output 1;
  错误输出:error output 2;
  打开文件后,新增文件绑定描述符可以依次增加;一条shell命令执行,会继承父进程的文件描述符。因此,所有运行的shell命令,都会有默认的3个文件描述符。
  Linux命令执行过程:

  命令执行过程:
  先有一个输入:输入可以从键盘,也可以从文件得到;
  命令执行完成:执行成功,会把结果输出到屏幕:standard output默认是屏幕;
  命令执行有错误:会把错误也输出到屏幕上面:standard error默认也是指的屏幕;
  文件输入输出由追踪为一个给定的进程所有打开文件的整数句柄来完成,这些数字值就是文件描述符。众所周知的文件描述符是 stdin, stdout 和 stderr,文件描述符的数字分别是0、1和2,这些数字和各自的设备是保留的。一个命令执行前,先会准备好所有输入输出,默认分别绑定(stdin,stdout,stderr),如果这个时候出现错误,命令将终止,不会执行。
  这些默认的输入、输出都是linux系统内定的,我们在使用过程中,有时候并不希望执行结果输出到屏幕,需要输出到文件或其它设备,这个时候就需要进行输出重定向了。
  linux shell下常用输入输出操作符:
  1、标准输入(stdin):代码为0,使用 < 或或 >>;
  /dev/stdout -> /proc/self/fd/1      1代表:/dev/stdout
  3、标准错误输出(stderr):代码为2,使用 2> 或 2>>;
  /dev/stderr -> /proc/self/fd/2      2代表:/dev/stderr
  输出重定向:
  实例:
  例1:
  # 把错误输出,不输出到屏幕,输出到err.txt
  ls test.sh test1.sh 1>suc.txt 2>err.txt
  cat suc.txt err.txt
  test.sh
  ls: test1.sh: 没有这个文件和目录
  # 继续追加把输出写入suc.txt err.txt“>>”追加操作符
  ls test.sh test1.sh 1>>suc.txt 2>>err.txt
  例2:
  # 将错误输出信息关闭掉
  ls test.sh test1.sh 2>&-
  test.sh
  ls test.sh test1.sh 2>/dev/null
  test.sh
  # & 代表是已经存在的文件描述符
  # &1 代表输出
  # &2 代表错误输出
  # &- 代表关闭与它绑定的描述符
  # /dev/null代表linux中的黑洞设备
  例3:
  # 关闭所有输出
  ls test.sh test1.sh 1>&- 2>&-
  # 关闭1,2 文件描述符
  ls test.sh test1.sh2>/dev/null 1>/dev/null
  # 将1,2 输出转发给/dev/null设备
  ls test.sh test1.sh >/dev/null 2>&1
  # 将错误输出2 绑定给正确输出1,然后将正确输出发送给/dev/null设备(常用)
  ls test.sh test1.sh &>/dev/null
  # & 代表标准输出 ,错误输出,将所有标准输出与错误输出发送到/dev/null文件
  注意:
  1、shell遇到">"操作符,会判断右边文件是否存在,如果存在就先删除,并且创建新文件,不存在直接创建,无论左边命令执行是否成功,右边文件都会变为空;
  2、">>"操作符判断右边文件,如果不存在,先创建;以添加方式打开文件,会分配一个文件描述符(不特别指定,默认为1,2),然后与左边的标准输出1或错误输出2绑定;
  3、当命令执行完,绑定文件的描述符也自动失效,0,1,2空闲;
  4、一条命令启动,命令的输入,正确输出,错误输出,默认分别绑定0,1,2文件描述符;
  5、一条命令在执行前,先会检查输出是否正确,如果输出设备错误,将不会进行命令执行。
  输入重定向:
  实例:
  例1:
  # cat 从test.sh 获得输入数据,然后输出给文件file
  cat > file < test.sh
  例2:
  cat > file suc.txt
  # 将接下来所有命令标准输出,输出到suc.txt文件
  ls -al
  # 执行命令,发现什么都不返回了,因为标准输出已经输出到suc.txt文件了
  exec 1>&6
  # 恢复标准输出
  exec 6>&-
  # 关闭fd 6描述符
  ls /proc/self/fd/
  0123
  说明:使用前先将标准输入保存到文件描述符6(文件描述符默认会打开0、1、2 还可以使用自定义描述符),然后对标准输出绑定到文件,接下来所有输出都会发生到文件,使用完后,恢复标准输出,关闭打开文件描述符6。
  例2:
  exec 3test.sh;
  # 打开test.sh可读写操作,与文件描述符3绑定
  while read line&-
  exec 3/dev/null 2>&1 与 2>&1 >/dev/null的区别
  1、command >/dev/null 2>&1
  # 相当于stdout="/dev/null"stderr="$stdout"
  # stderr也等于"/dev/null"
  # 结果是标准输出和标准错误都指向了/dev/null,也就是所有的输出都被丢弃了;
  2、command 2>&1 >/dev/null
  # 相当于stderr="$stdout"      // stderr指向了屏幕,因为stdout还是指向屏幕
  # stdout="/dev/null"            // stdout指向了/dev/null,但不会影响stderr的指向
  # 结果是标准错误仍然被打印到屏幕上,而标准输出被丢弃;
  三、管道与重定向的区别
  1、左边的命令应该有标准输出 | 右边的命令应该能接受标准输入;
  左边的命令应该有标准输出 > 右边只能是文件;
  左边的命令应该需要标准输入 < 右边只能是文件;
  2、管道触发两个子进程执行 "|" 两边的程序,而重定向是在一个进程内执行。
  实例:
  输入重定向:
  例1:
  cat test.sh | grep -n 'echo'
  3:   echo "nice";
  5:   echo "good";
  7:   echo "pass";
  9:   echo "shit";
  # "|" 管道两边都必须是shell命令
  例2:
  grep -n 'echo' < test.sh
  3:   echo "nice";
  5:   echo "good";
  7:   echo "pass";
  9:   echo "shit";
  # "重定向"符号,右边只能是文件(普通文件,文件描述符,文件设备)
  例3:
  (sed -n '1,$p' | grep -n 'echo') < test.sh
  3:   echo "nice";
  5:   echo "good";
  7:   echo "pass";
  9:   echo "shit";
  # 等同于sed -n '1,$p' < test.sh | grep -n 'echo'
  # 由于前面是管道,后面需要把test.sh内容重定向到sed,然后sed输出通过管道,输入给grep;需要将前面用"()"运算符括起来;在单括号内的命令,可以把它们看作一个命令;如果不加括号test.sh就是grep输入;
  # 重定向运算符,在shell命令解析前,首先检查的(一个命令,执行前一定检查好它的输入、输出,也就是0、1、2 设备是否准备好),所以优先级会最高。
  输出重定向:
  例1:
  cat test.sh > test.txt
  cat test.sh | tee test.txt &>/dev/null
  # 通过管道实现将结果存入文件,还需要使用命令tee,它把管道过来的标准输入写入test.txt,然后将标准输入复制到标准输出(stdout),重定向到/dev/null,所以不显示输出;
  # ">" 输出重定向,往往在命令最右边(也可以用到命令中间),接收左边命令的输出结果,重定向到指定文件。
  注:tee命令:输出屏幕同时输出到文件
  例2:
  ls test.sh test1.sh test2.sh 2>err.txt | grep 'test'
  test.sh
  test1.sh
  # 目录下面有:test、test1文件,test2.sh不存在,因此将ls命令错误输出输入到err.txt,正确输出会通过管道发送到grep命令;
  ls test.sh test1.sh test2.sh &>err.txt | grep 'test'
  # 输出结果为空,&代表正确与错误输出都输入给err.txt,通过管道传递的数据为空,所以无显示;
  # 同样 ">" 输出重定向符,优先级也是先解析,当一个命令有这个字符,它就会与左边命令标准输出绑定,准备好后,等待命令执行输出结果,然后接收。

页: [1]
查看完整版本: Linux shell管道与重定向及实例分析