概述

在linux中,通过bash-shell(后面简称bash)执行命令,与系统交互时,必然需要进行数据输入与输出。默认情况下,在用户执行命令时(执行命令结束,会关闭这三个文件),会打开三个文件:

  • 标准输入文件:从键盘输入的数据,会写入标准输入文件
  • 标准输出文件:命令非异常输出,会写入标准输出文件
  • 标准错误文件:命令异常输出,会写入标准错误输出文件

显示器会反复扫描标准输出文件和标准错误输出文件,一旦有数据,就会显示到屏幕上。接收标准输入的命令,命令执行时会去读取标准输入文件的内容。比如 cat 命令,只输入cat命令,cat命令会等待用户的输入,用户输入完成,cat命令立即会将输入的内容显示到屏幕上。

标准输入输出文件的位置

  1. [root@192 ~]# ll /dev/std*
  2. lrwxrwxrwx. 1 root root 15 Jan 23 10:45 /dev/stderr -> /proc/self/fd/2
  3. lrwxrwxrwx. 1 root root 15 Jan 23 10:45 /dev/stdin -> /proc/self/fd/0
  4. lrwxrwxrwx. 1 root root 15 Jan 23 10:45 /dev/stdout -> /proc/self/fd/1
  5. [root@192 ~]# ll /proc/self/fd/
  6. total 0
  7. lrwx------. 1 root root 64 Jan 25 14:14 0 -> /dev/pts/1
  8. lrwx------. 1 root root 64 Jan 25 14:14 1 -> /dev/pts/1
  9. lrwx------. 1 root root 64 Jan 25 14:14 2 -> /dev/pts/1
  10. lr-x------. 1 root root 64 Jan 25 14:14 3 -> /proc/101183/fd
  11. [root@192 ~]# ll /proc/self/fd/
  12. total 0
  13. lrwx------. 1 root root 64 Jan 25 14:14 0 -> /dev/pts/1
  14. lrwx------. 1 root root 64 Jan 25 14:14 1 -> /dev/pts/1
  15. lrwx------. 1 root root 64 Jan 25 14:14 2 -> /dev/pts/1
  16. lr-x------. 1 root root 64 Jan 25 14:14 3 -> /proc/101201/fd

重定向的概念

所谓重定向,就是改变命令默认的输入或输出方式待深入。(PS,好像理解的有问题,目前弄不懂)。
如将原本输出到标准输出文件的,改变为输出到普通文件,这就是输出重定向。同理,将原本从标准输入文件中获取数据的,改变为从普通文件中获取数据。还有,将一个命令的标准输出作为另外一个命令的标准输入。

  1. [root@192 ~]# cat <list.txt | cat;cat;
  2. total 8
  3. -rwxr-xr-x. 1 root root 1670 Jan 24 13:50 download.sh
  4. -rw-r--r--. 1 root root 0 Jan 24 14:11 list.txt
  5. drwxr-xr-x. 2 root root 77 Jan 24 10:49 script
  6. -rwxr-xr-x. 1 root root 57 Jan 24 13:54 ssh.sh
  7. kkk
  8. kkk
  9. # 等待用户输入

cat默认从标准输入文件中获取数据,利用 <list.txt 将其获取数据的方式改为从文件list.txt中获取。cat命令默认将获取的数据,输出到标准输出文件,但通过管道符 | 将cat默认输出行为,重定向到标准输入文件中,供下一个命令使用。
管道符后有两个cat命令,第一个命令读出标准输入文件中的内容,由于标准输入文件中的数据已经被读出,第二个cat命令,等待用户的输入。
另外,一定要理解,对于命令而言,就是接收数据,然后处理数据并返回处理的结果。也就是说,

常见重定向操作

  • cmd1 | cmd2:管道;将cmd1的标准输出作为cmd2的标准输入。特别注意,标准错误输出不会被用作cmd2的标准输入。
  • > file:将命令的标准输出指向文件
  • < file:将命令的标准输入指向文件
  • >> file:将标准输出指向文件; 追加的方式,而非直接覆盖文件
  • >| file:将标准输出指向文件,在设置noclobber的情况下,强制覆盖文件.

    1. [root@192 ~]# set -o noclobber # 开启不允许重定向覆盖
    2. [root@192 ~]# echo aa > a.txt
    3. -bash: a.txt: cannot overwrite existing file # 会报错
    4. [root@192 ~]# echo aa >| a.txt # 不会报错
  • n>| file:在设置noclobber的情况下,将文件描述符n指向file

  • <> file:将标准输入和输出均指向file。相当于1>file 0<file
  • n<> file:将文件描述符n,既作为输出又作为输入。

我理解的,文件描述符本质是一下链接文件,这些链接文件或可读或可写或可读写。如果文件描述符可读,表示作为输入,可写表示作为输出,可读可写表示既能是输出又能是输入。对应的,> 可以使文件描述符可写,< 可以使文件描述符可读,<>可以使文件描述符可读可写。

  • << label:Here文档,从label开始,遇到label结束,期间的所有字符均作为标准输入。
  • n > file:使文件描述符n指向file,作为输出
  • n < file:使文件描述符n指向file,作为输入
  • n >> file:使文件描述符n指向file,作为追加输出
  • n>&1:复制标准输出到文件描述符n。经过测试1省略会报错
  • n<&0:复制标准输入到文件描述符n。经过测试1省略会报错
  • n>&m:同 n>&1,此处的m=1
  • n<&m:同 n<&0,此处的m=0
  • &>file:将标准输出和标准错误输出指向file
  • <&-:关闭标准输入
  • >&-:关闭标准输出
  • n>&-:关闭文件描述符n的输入
  • n<&-:闭文件描述符n的输出
  • n>&word:If n is not specified, the standard output (file descriptor 1) is used. If the digits in word do not specify a file descriptor open for output, a redirection error occurs. As a special case, if n is omitted, and word does not expand to one or more digits, the standard output and standard error are redirected as described previously.
  • n<&word:If word expands to one or more digits, the file descriptor denoted by n is made to be a copy of that file descriptor. If the digits in word do not specify a file descriptor open for input, a redirection error occurs. If word evaluates to -, file descriptor n is closed. If n is not specified, the standard input (file descriptor 0) is used.
  • n>&digit-:Moves the file descriptor digit to file descriptor n, or the standard output (file descriptor 1) if n is not specified.
  • n<&digit-:Moves the file descriptor digit to file descriptor n, or the standard input (file descriptor 0) if n is not specified. digit is closed after being duplicated to n.

    重定向命令几个知识点

    重定向命令会创建新文件
    一个命令可以同时使用多个重定向命令,最后一个有效 ```shell [root@192 data]# echo 123 >a.txt >b.txt [root@192 data]# ls # 创建了新的文件a.txt b.txt a.txt b.txt demo.txt find.txt [root@192 data]# cat a.txt [root@192 data]# cat b.txt # 最终数据输出到了b.txt 123 [root@192 data]#
  1. <a name="afMKE"></a>
  2. # 深入理解重定向
  3. <a name="uz6Lt"></a>
  4. ## 以下结论,是经过试验总结而来,不一定准确。
  5. 命令不管输出数据到文件(包含普通文件或设备文件),还是从文件中获取数据。都需要得到文件的路径,然后写或读该文件。在这个前提下,会有这样一个疑问?同一个命令,比如echo,第一次执行,我希望命令将数据输出到屏幕,第二次执行,我希望命令将数据输出到普通文件。显然,输出到屏幕和输出到普通文件,是将数据写入两个不同文件,如何比较方便的切换写入的文件呢?当然,传入文件路径是个解决方案,也是比较容易想到的方案。下面,我们看看shell采用的方案。<br />定义一个或多个公共的软连接文件,命令需要写入或读取文件的时候,通过这些公共软连接来操作文件。<br />shell程序定义了三个这样的公共的软连接文件<br />标准输入 默认情况下,指向键盘<br />标准输出 默认情况下,指向屏幕<br />标准错误输出 默认情况下,指向屏幕<br />在shell中运行的命令,如果需要将数据输出数据,就操作(写)标准输出文件;需要从获取数据,就操作标准输入文件。如上,标准输出文件,默认情况下,指向屏幕,所以默认情况下,写入标准输出的文件的数据,会显示到屏幕上。其他两个标准输出也规定了默认指向,这里不再赘述。<br />如果命令想输出或输入到普通文件,只要改变标准输出或标准输入文件的指向即可。而修改标准输入或输出文件的指向,就是重定向。<br />先看一下,shell中标准输入输出文件的位置<br />/dev/stderr<br />/dev/stdin<br />/dev/stdout<br />以上三个文件,指向下面三个文件<br />/proc/self/fd/2<br />/proc/self/fd/0<br />/proc/self/fd/1<br />上面这三个文件,是在打开shell的就默认设置好的。这几个文件,叫做文件描述符。
  6. ```shell
  7. [root@192 ~]# ll /dev/std*
  8. lrwxrwxrwx. 1 root root 15 Mar 19 20:29 /dev/stderr -> /proc/self/fd/2
  9. lrwxrwxrwx. 1 root root 15 Mar 19 20:29 /dev/stdin -> /proc/self/fd/0
  10. lrwxrwxrwx. 1 root root 15 Mar 19 20:29 /dev/stdout -> /proc/self/fd/1
  11. [root@192 ~]# ll /proc/self/fd/
  12. total 0
  13. lrwx------. 1 root root 64 Mar 20 12:09 0 -> /dev/pts/0
  14. lrwx------. 1 root root 64 Mar 20 12:09 1 -> /dev/pts/0
  15. lrwx------. 1 root root 64 Mar 20 12:09 2 -> /dev/pts/0
  16. lr-x------. 1 root root 64 Mar 20 12:09 3 -> /proc/1925/fd
  17. [root@192 ~]#

修改标准文件的指向的方式,也就是重定向命令的输入输出

临时修改

语法:command 重定向操作。具体操作,见 常见重定向操作
只对该命令此次运行有效

永久修改

语法:exec 重定向操作。其中具体操作,见 常见重定向操作 (PS:该列表中操作,有些不适用该命令)。常见操作有 exec n>m; exec n

操作示例

新建文件描述符

  1. [root@192 ~]# ll /proc/self/fd/ # 列出当前shell中现有的文件描述符
  2. total 0
  3. lrwx------. 1 root root 64 Mar 20 12:26 0 -> /dev/pts/0
  4. lrwx------. 1 root root 64 Mar 20 12:26 1 -> /dev/pts/0
  5. lrwx------. 1 root root 64 Mar 20 12:26 2 -> /dev/pts/0
  6. lr-x------. 1 root root 64 Mar 20 12:26 3 -> /proc/1953/fd
  7. [root@192 ~]# exec 4>./demo.txt
  8. [root@192 ~]# exec 5>./demo.txt
  9. [root@192 ~]# ll /proc/self/fd/
  10. total 0
  11. lrwx------. 1 root root 64 Mar 20 12:27 0 -> /dev/pts/0
  12. lrwx------. 1 root root 64 Mar 20 12:27 1 -> /dev/pts/0
  13. lrwx------. 1 root root 64 Mar 20 12:27 2 -> /dev/pts/0
  14. lr-x------. 1 root root 64 Mar 20 12:27 3 -> /proc/1955/fd
  15. l-wx------. 1 root root 64 Mar 20 12:27 4 -> /root/demo.txt # 新生成的文件描述符
  16. l-wx------. 1 root root 64 Mar 20 12:27 5 -> /root/demo.txt # 4 5 可写,不可写,为什么?
  17. [root@192 ~]# exec 5<./demo.txt # 重置文件描述符
  18. [root@192 ~]# ll /proc/self/fd/
  19. total 0
  20. lrwx------. 1 root root 64 Mar 20 12:27 0 -> /dev/pts/0
  21. lrwx------. 1 root root 64 Mar 20 12:27 1 -> /dev/pts/0
  22. lrwx------. 1 root root 64 Mar 20 12:27 2 -> /dev/pts/0
  23. lr-x------. 1 root root 64 Mar 20 12:27 3 -> /proc/1956/fd
  24. l-wx------. 1 root root 64 Mar 20 12:27 4 -> /root/demo.txt
  25. lr-x------. 1 root root 64 Mar 20 12:27 5 -> /root/demo.txt # 重置后,可读,不可写
  26. [root@192 ~]# exec 6<>./demo.txt # 创建6
  27. [root@192 ~]# ll /proc/self/fd/
  28. total 0
  29. lrwx------. 1 root root 64 Mar 20 12:32 0 -> /dev/pts/0
  30. lrwx------. 1 root root 64 Mar 20 12:32 1 -> /dev/pts/0
  31. lrwx------. 1 root root 64 Mar 20 12:32 2 -> /dev/pts/0
  32. lr-x------. 1 root root 64 Mar 20 12:32 3 -> /proc/1980/fd
  33. l-wx------. 1 root root 64 Mar 20 12:32 4 -> /root/demo.txt
  34. lr-x------. 1 root root 64 Mar 20 12:32 5 -> /root/demo.txt
  35. lrwx------. 1 root root 64 Mar 20 12:32 6 -> /root/demo.txt # 可读,可写
  36. [root@192 ~]#

修改标准输入文件到普通文件

  1. [root@192 ~]# exec 1>./demo.txt # 修改标准输出的指向
  2. [root@192 ~]# ll /proc/self/fd/ # 可以看出,该命令没有任何返回
  3. [root@192 ~]# echo ceshi # 也没有返回
  4. [root@192 ~]# exec 1>&2 # 将标准输出重新指定到屏幕
  5. [root@192 ~]# cat demo.txt # 显示输出结果,可以看到,文件中写入了刚才命令的输出
  6. total 0
  7. lrwx------. 1 root root 64 Mar 20 12:51 0 -> /dev/pts/0
  8. l-wx------. 1 root root 64 Mar 20 12:51 1 -> /root/demo.txt # 这地方也可以看出,指向改了
  9. lrwx------. 1 root root 64 Mar 20 12:51 2 -> /dev/pts/0
  10. lr-x------. 1 root root 64 Mar 20 12:51 3 -> /proc/2008/fd
  11. l-wx------. 1 root root 64 Mar 20 12:51 4 -> /root/demo.txt
  12. lr-x------. 1 root root 64 Mar 20 12:51 5 -> /root/demo.txt
  13. lrwx------. 1 root root 64 Mar 20 12:51 6 -> /root/demo.txt
  14. ceshi
  15. [root@192 ~]#

有几个注意事项:

  • < > 这两个符号叫,文件描述符的操作符。该操作符左右,最好不要有空格,要不然,很容易出现奇怪的情况。
  • 操作符 left>right 表示修改输出指向,left默认为1,right不能是链接(PS:个人认为&2,就是求链接文件的指向的真实文件)。
  • 操作符 left<right 表示修改输入指向,left默认0,right不能是链接(同上)。
  • 不要尝试 exec 2>普通文件,会炸,原因未知

补充

/proc/self/的理解

proc 可能是process的缩写,即启动进程时用到数据会放到这个目录里

  1. [root@localhost proc]# ls
  2. 1 13331 22 289 392 41 578 646 95 dma keys mpt swaps
  3. 10 13349 23 292 393 43 579 654 acpi driver key-users mtrr sys
  4. 11 14 24 293 394 44 580 7 asound execdomains kmsg net sysrq-trigger
  5. 1109 15 272 30 395 45 581 7566 buddyinfo fb kpagecount pagetypeinfo sysvipc
  6. 11423 16 273 31 396 47 6 8 bus filesystems kpageflags partitions timer_list
  7. 12837 1640 274 32 397 482 60 891 cgroups fs loadavg sched_debug timer_stats
  8. 13 17 275 33 398 508 603 892 cmdline interrupts locks schedstat tty
  9. 13238 18 283 367 399 518 626 897 consoles iomem mdstat scsi uptime
  10. 13241 19 284 368 4 573 627 9 cpuinfo ioports meminfo self version
  11. 13245 2 286 377 400 574 629 929 crypto irq misc slabinfo vmallocinfo
  12. 13325 20 287 378 401 576 633 932 devices kallsyms modules softirqs vmstat
  13. 13326 21 288 391 402 577 645 937 diskstats kcore mounts stat zoneinfo

文件夹里有很多数字文件夹,这些数字就是正在运行的进程的进程ID。以进程ID命名的文件夹,里面如果有fd文件夹。fd文件夹中一般存放的是当前命令用到的文件描述符,一般默认打开0 1 2三个文件描述符。这里的文件描述符也都一些链接文件。

  1. [root@localhost proc]# ls 1/fd
  2. 0 10 12 19 20 22 24 26 28 3 31 33 35 37 39 40 42 46 50 54 7 9
  3. 1 11 14 2 21 23 25 27 29 30 32 34 36 38 4 41 45 5 52 6 8

proc文件夹下,有一个特殊的文件self

  1. [root@localhost proc]# ls -lh sel*
  2. lrwxrwxrwx. 1 root root 0 Mar 8 20:24 self -> 13353
  3. [root@localhost proc]#

可见,是一个链接文件,指向某个进程ID命名的文件夹。如上,13353这个进程ID就是,命令 ls -lh sel 的进程ID。如果你重复执行, ls -lh sel 你会发现,这个指向会变。
综上,被执行的任何命令,都会产生自己的进程ID。而/proc/self 这个链接文件指向当前正在运行的进程ID命令的文件夹,文件中存放命令所需的文件描述符。
重定向操作符 < > 就是用来操作这些文件描述符的。可以修改他们的指向,也可以修改他们的可读性。