1.对重定向的理解

  • Linux Shell 重定向分为两种,一种输入重定向,一种是输出重定向;从字面上理解,输入输出重定向就是「改变输入与输出的方向」的意思。
  • 输入方向就是数据从哪里流向程序。标准输入方向是指数据从键盘流向程序,如果改变了它的方向,数据就从其它地方流入,这就是输入重定向。
  • 输出方向就是数据从程序流向哪里。标准输出方向是指数据从程序流向显示器,如果改变了它的方向,数据就流向其它地方,这就是输出重定向。

2.硬件设备和文件描述符

计算机的硬件设备有很多,常见的输入设备有键盘、鼠标、麦克风、手写板等,输出设备有显示器、投影仪、打印机等。不过,在 Linux 中,标准输入设备指的是键盘,标准输出设备指的是显示器
Linux系统中把一切都看做文件,包括普通文件-、目录文件d、字符设备文件c、块设备文件b、符号链接文件l以及标准输入设备(键盘)和标准输出设备(显示器)在内的所有计算机硬件都是文件

文件描述符是内核为了高效管理已被打开的文件所创建的索引(一个非负整数),用于指代已被打开的文件。

Linux下所有的的I/O操作的系统调用都是通过文件描述符执行,一个文件描述符只是一个和打开的文件相关联的整数,它的背后可能是一个硬盘上的普通文件、FIFO、管道、终端、键盘、显示器,甚至是一个网络连接。例如0表示标准输入(键盘)、1表示标准输出(显示器)、2表示标准错误(显示器),文件描述符会在这个基础上递增。
stdin、stdout、stderr 默认都是打开的,在重定向的过程中,0、1、2 这三个文件描述符可以直接使用。

表1:与输入输出有关的文件描述符

文件描述符 文件名 类型 硬件
0 stdin 标准输入文件 键盘
1 stdout 标准输出文件 显示器
2 stderr 标准错误输出文件 显示器

文件描述符到底是什么

文件描述符: 在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。
一个 Linux 进程启动后,会在内核空间中创建一个 PCB 控制块,PCB 内部有一个文件描述符表(File descriptor table),记录着当前进程所有可用的文件描述符,也即当前进程所有打开的文件。

除了文件描述符表,系统还需要维护另外两张表:

  • 打开文件表(Open file table)
  • i-node 表(i-node table)

文件描述符表每个进程都有一个,打开文件表和 i-node 表整个系统只有一个,它们三者之间的关系如下图所示。

文件描述符表: 进程级的列表,也就是用户区的一部分,进程每打开一个文件就会新建一个文件描述符。
系统级打开文件表: 系统级的列表,对当前系统的所有进程都共享,每条条目包含文件偏移量、访问模式以及指向它的文件描述符的条目计数
文件系统索引节点表: inode索引节点表(UID、GID、ctime、mtime、atime、读写执行权限、链接数、block位置)

进程级文件描述符 系统级打开文件表 i-node表

1.文件描述符标志

2.文件指针(open file handle)

通过文件描述符,可以找到文件指针,从而进入打开文件表

1.文件偏移量,也就是文件内部指针偏移量。调用 read() 或者 write() 函数时,文件偏移量会自动更新,当然也可以使用 lseek() 直接修改。

2.状态标志,比如只读模式、读写模式、追加模式、覆盖模式等。

3.i-node 表指针。

要想真正读写文件,要通过打开文件表的 i-node 指针进入 i-node 表

1.文件类型,例如常规文件、套接字或 FIFO。

2.文件大小。

3.时间戳,比如创建时间、更新时间。

4.文件锁。

对上图的进一步说明:

  • 在进程 A 中,文件描述符 1 和 20 都指向了同一个打开文件表项,标号为 23(指向了打开文件表中下标为 23 的数组元素),这可能是通过调用 dup()、dup2()、fcntl() 或者对同一个文件多次调用了 open() 函数形成的。
  • 进程 A 的文件描述符 2 和进程 B 的文件描述符 2 都指向了同一个文件,这可能是在调用 fork() 后出现的(即进程 A、B 是父子进程关系),或者是不同的进程独自去调用 open() 函数打开了同一个文件,此时进程内部的描述符正好分配到与其他进程打开该文件的描述符一样。
  • 进程 A 的描述符 0 和进程 B 的描述符 3 分别指向不同的打开文件表项,但这些表项均指向 i-node 表的同一个条目(标号为 1976);换言之,它们指向了同一个文件。发生这种情况是因为每个进程各自对同一个文件发起了 open() 调用。同一个进程两次打开同一个文件,也会发生类似情况。

有了以上对文件描述符的认知,我们很容易理解以下情形:

  • 同一个进程的不同文件描述符可以指向同一个文件;
  • 不同进程可以拥有相同的文件描述符;
  • 不同进程的同一个文件描述符可以指向不同的文件(一般也是这样,除了 0、1、2 这三个特殊的文件);
  • 不同进程的不同文件描述符也可以指向同一个文件。

文件描述符、文件、进程之间的关系

  • 每个文件描述符都指向一个打开的文件相对应

  • 不同的文件描述符可能指向同一个打开的文件

  • 相同的文件可能被不同的进程打开,也可以在被同一个进程打开多次

Linux Shell 输出重定向

输出重定向是指命令的结果不再输出到显示器上,而是输出到其它地方,一般是文件中。这样做的最大好处就是把命令的结果保存起来,当我们需要的时候可以随时查询。Bash 支持的输出重定向符号如下表所示。
在输出重定向中,>代表的是覆盖,>>代表的是追加。

注意点:

  1. 输出重定向的写法是fd>file或者fd>>file,其中 fd 表示文件描述符,如果不写,默认为 1,也就是标准输出文件。即command 1>file与command >file相同,当文件描述符为大于 1 的值时,比如 2,就必须写上。
  2. fd>之间不能有空格,否则 Shell 会解析失败;>file之间的空格可有可无
  3. /dev/null 文件。如果想要将输出结果丢弃,可以将命令结果重定向到 /dev/null 文件中,即 ls -l &>/dev/null,任何放入垃圾箱的数据都会被丢弃,不能恢复。
  命令 结果 实际执行
1>之间有空格

echo "c.biancheng.net" 1 >log.txt

cat log.txt

c.biancheng.net 1 echo "c.biancheng.net" 1 1>log.txt

表2:Bash 支持的输出重定向符号

类 型 符 号 作 用 举栗
标准输出重定向 command >file 以覆盖的方式,把 command 的正确输出结果输出到 file 文件中。

ls -l命令的输出结果重定向到文件中。

[c.biancheng.net]$ ls -l  #先预览一下输出结果
总用量 16
drwxr-xr-x. 2 root root 21 7月 1 2016 abc
-rw-r--r--. 1 mozhiyan mozhiyan 399 3月 11 17:12 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan 67 3月 22 17:16 demo.txt [c.biancheng.net]$ ls -l >demo.txt #重定向
[c.biancheng.net]$ cat demo.txt #查看文件内容
总用量 12
drwxr-xr-x. 2 root root 21 7月 1 2016 abc
-rw-r--r--. 1 mozhiyan mozhiyan 399 3月 11 17:12 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan 0 3月 22 17:21 demo.txt
command >>file 以追加的方式,把 command 的正确输出结果输出到 file 文件中。

将 echo 命令的输出结果以追加的方式写入到 demo.txt 文件中。

  1. #!/bin/bash
  2. for str in "C语言中文网" "http://c.biancheng.net/" "成立7年了"
  3. do
  4.   echo $str >>demo.txt #将输入结果以追加的方式重定向到文件
  5. done

运行脚本,查看文件demo.txt内容,显示如下:
C语言中文网
http://c.biancheng.net/
成立7年了

标准错误输出重定向 command 2>file 以覆盖的方式,把 command 的错误信息输出到 file 文件中。

命令正确执行是没有错误信息的,我们必须刻意地让命令执行出错,如下所示:

[c.biancheng.net]$ ls java  #先预览一下错误信息
ls: 无法访问java: 没有那个文件或目录
[c.biancheng.net]$ ls java 2>err.log #重定向
[c.biancheng.net]$ cat err.log #查看文件
ls: 无法访问java: 没有那个文件或目录
command 2>>file 以追加的方式,把 command 的错误信息输出到 file 文件中。  
正确输出和错误信息同时保存 command >file 2>&1 覆盖的方式,把正确输出和错误信息同时保存到同一个文件(file)中。

正确输出和错误信息同时保存

【实例1】把正确结果和错误信息都保存到一个文件中,例如:

[c.biancheng.net]$ ls -l >out.log 2>&1
[c.biancheng.net]$ ls java >>out.log 2>&1
[c.biancheng.net]$ cat out.log
总用量 12
drwxr-xr-x. 2 root root 21 7月 1 2016 abc
-rw-r--r--. 1 mozhiyan mozhiyan 399 3月 11 17:12 demo.sh
-rw-rw-r--. 1 mozhiyan mozhiyan 278 3月 16 17:17 main.c
ls: 无法访问java: 没有那个文件或目录

out.log 的最后一行是错误信息,其它行都是正确的输出结果。

command >>file 2>&1 追加的方式,把正确输出和错误信息同时保存到同一个文件(file)中。  
command >file1 2>file2 以覆盖的方式,把正确的输出结果输出到 file1 文件中,把错误信息输出到 file2 文件中。  
command >>file1  2>>file2 以追加的方式,把正确的输出结果输出到 file1 文件中,把错误信息输出到 file2 文件中。

上面的实例将正确结果和错误信息都写入同一个文件中,这样会导致视觉上的混乱,不利于以后的检索,

所以我建议把正确结果和错误信息分开保存到不同的文件中,也即写成下面的形式:

ls -l >>out.log 2>>err.log

这样一来,正确的输出结果会写入到 out.log,而错误的信息则会写入到 err.log。

command >file 2>file 不推荐】这两种写法会导致 file 被打开两次,引起资源竞争,所以 stdout 和 stderr 会互相覆盖  
command >>file 2>>file  

Linux Shell 输入重定向

输入重定向就是改变输入的方向,不再使用键盘作为命令输入的来源,而是使用文件作为命令的输入。
和输出重定向类似,输入重定向的完整写法是fd<file,其中 fd 表示文件描述符,如果不写,默认为 0,也就是标准输入文件。

表3:Bash 支持的输出重定向符号

符号 说明 举栗
command <file 将 file 文件中的内容作为 command 的输入。 统计 readme.txt 文件中有多少行文本:

[c.biancheng.net]$ cat readme.txt  #预览一下文件内容
C语言中文网
http://c.biancheng.net/
成立7年了
[c.biancheng.net]$ wc -l <readme.txt #输入重定向
3
command <<END

从标准输入(键盘)中读取数据,直到遇见分界符 END 才停止

(分界符可以是任意的字符串,用户自己定义)。

输入重定向符号<<,这个符号的作用是使用特定的分界符作为命令输入的结束标志,而不使用 Ctrl+D 键。

<<之后的分界符可以自由定义,只要再碰到相同的分界符,两个分界符之间的内容将作为命令的输入(不包括分界符本身)

统计用户在终端输入的文本的行数。

[c.biancheng.net]$ wc -l <<END

> 123
> 789
> abc
> END
3

wc 命令会一直等待用输入,直到遇见分界符 END 才结束读取。

command <file1 >file2 将 file1 作为 command 的输入,并将 command 的处理结果输出到 file2。  

代码块重定向

{}<file1

  逐行读取文件内容。

#!/bin/bash

while read str; do
echo $str
done <readme.txt

运行结果:
C语言中文网
http://c.biancheng.net/
成立7年了
日IP数万

总结:

1>log.txt 2>&1 将输出全重定向到log.txt中
  • 本来1----->屏幕 (1指向屏幕)
  • 执行>log后, 1----->log (1指向log)
  • 执行2>&1后, 2----->1 (2指向1,而1指向log,因此2也指向了log)
可以缩写为&>log 或者 >&log
 
2>&1 1>log.txt 将标准错误输出重定向到屏幕,将标准输出重定向到log.txt中
  • 本来1----->屏幕 (1指向屏幕)
  • 执行2>&1后, 2----->1 (2指向1,而1指向屏幕,因此2也指向了屏幕)
  • 执行>log后, 1----->log (1指向log,2还是指向屏幕)
>/dev/null 2>&1  将输出全定向到/dev/null
  • /dev/null 代表空设备文件
  • 2 表示stderr标准错误,& 表示等同于的意思,2>&1,表示2的输出重定向等同于1
  • 1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于 "1>/dev/null"
最常用的方式有:command > file 2>file  与command > file 2>&1
它们有什么不同的地方吗?
首先command >file 2>file 的意思是将命令所产生的stdout和stderr送到file中,file会被打开两次,stdout和stderr会互相覆盖,这样写相当使用了FD1和FD2两个同时去抢占file 的管道
command >file 2>&1 这条命令将stdout直接送向file,stderr 继承了FD1管道后再被送往file,此时,file 只被打开了一次,也只使用了一个管道FD1,它包括了stdout和stderr的内容。
从IO效率上,前一条命令的效率要比后面一条的命令效率要低,所以在编写shell脚本的时候,较多的时候我们会command > file 2>&1 这样的写法。

参考文献: https://www.cnblogs.com/yujianfei/p/9068472.html

shell 重定向以及文件描述符的更多相关文章

  1. 18 shell 重定向以及文件描述符

    1.对重定向的理解 2.硬件设备和文件描述符 文件描述符到底是什么 3.Linux Shell 输出重定向 4.Linux Shell 输入重定向 5.结合Linux文件描述符谈重定向 6.Shell ...

  2. linux shell exec 关联文件描述符

    在写shell脚本时,如果多个命令的输入或输出都是同一个文件,而这个文件的路径和名字都很长,则需要书写很多次同样的路径会很浪费时间,我们可以使用exec命令来关联一个自定义的文件描述符到一个特定的文件 ...

  3. Linux Shell 文件描述符 及 stdin stdout stderr 重定向

    Abstract: 1) Linux Shell 命令的标准输入.标准输出.标准错误,及其重定位: 2)Linux Shell 操作自定义文件描述符: 文件描述符是与文件相关联的一些整数,他们保持与已 ...

  4. 自学Linux Shell14.2-在脚本中使用其他文件描述符

    点击返回 自学Linux命令行与Shell脚本之路 14.2-在脚本中使用其他文件描述符 在脚本中重定向输入和输出,并布局限于以上讲的3个默认的文件描述符,shell最多可以有9个打开的文件描述符.这 ...

  5. exec操作文件描述符

    exec命令可以用来替代当前shell:换句话说,并没有启动子shell.使用这一命令时任何环境都将被清除,并重新启动一个shell. 它的一半形式为: exec command 其中,command ...

  6. Unix系统编程()复制文件描述符

    Bourne shell的IO重定向语法2>&1,意在通知shell把标准错误(文件描述符2)重定向到标准输出(文件描述符1).因此下列命令将把标准输出和标准错误写入result.log ...

  7. linux中文件描述符

    :: # cat ping.txt PING baidu.com (() bytes of data. bytes from ttl= time=32.1 ms bytes from ttl= tim ...

  8. Shell重定向文件描述符

    #!/bin/bash      最近在看shell,各种困惑,不过解决困惑的感觉还是很不错的.废话少说,linux中使用文件描述符来标识每个文件对象.文件描述符为一个非负整数,可以唯一标识会话中打开 ...

  9. 【Shell脚本学习指南笔记】重定向文件描述符 2>&1

    如: make > results 2>&1 重定向 > results让文件描述符1(标准输出)作为文件results,接下来的重定向2>&1有两个部分.2& ...

随机推荐

  1. Linux中级之ansible概念及hoc命令行调用模式

    一.Ansible简介 ansible是新出现的开源的自动化运维工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fabric)的优点,实现了批量系统 ...

  2. java 集合梳理

    使用 processOn 画的java 集合图谱,应付面试应该可以了

  3. Redis SWAPDB 命令背后做了什么

    Redis SWAPDB 命令背后做了什么 目录 Redis SWAPDB 命令背后做了什么 0x00 摘要 0x01 SWAPDB 基础 1.1 命令说明 1.2 演示 0x02 预先校验 0x03 ...

  4. EdgeX Foundry试运行

    EdgeX Foundry试运行 简介 EdgeX Foundry是一个由Linux基金会发起的,且厂商中立的开源IoT边缘计算项目.它可以采集来自多个源的数据,并将这些数据转发到一个中央系统.Edg ...

  5. [leetcode] 875. 爱吃香蕉的珂珂(周赛)

    875. 爱吃香蕉的珂珂 这题时间要求比较严格... 首先,将piles排序,然后二分查找. 总之,答案K肯定位于piles[?]piles[?+1]或者1piles[0]之间 所以我们先二分把?找到 ...

  6. python 获取当天和前几天时间数据

    python 获取当天和前几天时间数据 import datetime from datetime import datetime, date, timedelta def dayDateRange( ...

  7. Python+Selenium学习笔记11 - python官网的tutorial - 定义函数

    1 def f(a, L=[]): 2 L.append(a) 3 return L 4 5 print f(5) 6 print f(2) 输出 1 def f(a, L=None): 2 if L ...

  8. Consistent 与 Mirrored 视角

    Consistent 与 Mirrored 视角 在进行分布式训练时,OneFlow 框架提供了两种角度看待数据与模型的关系,被称作 consistent 视角与 mirrored 视角. 本文将介绍 ...

  9. 什么是GStreamer?

    什么是GStreamer? GStreamer是用于创建流媒体应用程序的框架.基本设计来自俄勒冈大学研究生院的视频管道以及DirectShow的一些想法. GStreamer的开发框架使编写任何类型的 ...

  10. TOF摄像机可以替代Flash激光雷达吗?

    TOF摄像机可以替代Flash激光雷达吗? 一.基于ToF技术的Flash激光雷达 基本成像原理上ToF Camera与LiDAR相同,都采用飞行时间测距技术(包括利用APD或SPAD的直接测距法,和 ...