linux下的IO重定向与管道相关的知识简析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux下的IO重定向与管道相关的知识简析相关的知识,希望对你有一定的参考价值。

一、bash重定向部分简单翻译

1.1、bash手册关于重定向短短的注解(因为过于经典,所以摘录出来)

我的翻译要开始毁经典啦...

?

  • 重定向概述

技术分享图片
? ? ? ?在shell接口执行命令前,命令的输入与输出可能会使用一个特别的标记符号来解释它们的重定向。重定向也应用于在当前的shell环境下打开和关闭文件。重定向的标记符号是按照从左到右的出现的顺序来处理的。
?
? ? ? ?请看这两种形式的表示 {varname}> file , 2>file.很多情况,在重定向符号之前会有一个文件描述符(具体何为文件描述符,后面我们会简单说明),这个描述符通常为一个数字,当然,也可能是一个由符号{}引用起来的一个字符串,这种情况下,除了>&-,<&-之外的重定向操作(>&-,<&-这两个与关闭文件描述符相关的),shell会给{varname}分配一个大于10的数字(0-9这10个数字已经预先定义了,有其特定含义和用途),如果{varname}>&-,{varname}<&-是这两种形式,表示关闭分配给{varname}的文件描述符。

?
? ? ? ?在下面的描述中,如果一个重定向符号前面的文件描述符编号省略了,而且重定向的第一个字符是"<",就表示这个重定向为标准输入(文件描述符为0)。如果第一个字符是">",就表示这个重定向为标准输出(文件描述符为1)。
?
? ? ? ?在下面关于文件重定向的操作的描述中,重定向运算后的单词,如果没有特殊说明,要进行bash的展开操作,大括号展开,波浪号展开,参数替换展开,命令替换展开,算术运算展开,引用去掉,路径展开以及单词拆分等。如果展开后有多个单词,bash就会出错。

小贴士
    这里翻译只是字面的翻译,如果你对bash的展开特性(brace expansion,tilde expansion,parameter expansion,command substitution,arthmetic expansion,quote removal,pathname expansion,word splitting )不太了解,而又纠结这个东西,我建议你先去了解bash的expansion(展开)特性以及它们在shell执行命令的替换的先后顺序。

?
    注意重定向符号出现的先后顺序,它是至关重要的。例如下面的命令:

ls > dirlist 2>&1

    命令的意思大概是把指令ls的标准输出和标准错误重定向到文件dirlist中。而我们稍微改变一下命令中重定向符号出现的先后顺序,请看:

ls 2>&1 > dirlist

    ls加上后边部分的东西的标准输出被写入文件dirlist中,因为在ls的标准输出在被重定向到文件dirlist前,ls的标准错误输出已经被复制到标准输出中了。
?

? ? ?当bash使用重定向的时候,它会对下面的列出的几个文件特殊处理:
? ? ?几种形式如下:
? ? ?? ? ?1.** /dev/fd/fd**
? ? ?? ? ?2. **/dev/stdin**
? ? ?? ? ?3. **/dev/stdout**
? ? ?? ? ?4.**/dev/stderr**
? ? ?? ? ?5.** /dev/tcp/host/port**
? ? ?? ? ?6. **/dev/udp/host/port**

? ? ?小贴士:/dev/fd是一个目录,设备文件(文件描述符相关的)。后边的fd是一个整数,表示复制文件描述符fd;

        第一种情况:(/dev/fd/fd)

如果最后一个fd是一个整数,则复制文件描述符fd。请看服务器上默认的:
[[email protected] ~]# ls -l /dev/fd/[0-2]
lrwx------ 1 root root 64 Sep  8 16:40 /dev/fd/0 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  8 16:40 /dev/fd/1 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  8 16:40 /dev/fd/2 -> /dev/pts/0

        第二种情况:(/dev/stdin)

复制文件描述符0.服务器存储文件路径/dev/stdin (设计要遵循unix万物皆文件的哲学思想,)
[[email protected] ~]# ls -l /dev/stdin
lrwxrwxrwx 1 root root 15 Sep  5 09:38 /dev/stdin -> /proc/self/fd/0

        第三种情况:(/dev/stdout)

复制文件描述符1(/dev/stdout).

        第四种情况:(/dev/stderr)

复制文件描述2(/dev/stderr)

        第五种情况:(/dev/tcp/host/port)

host表示主机名或者ip地址,port表示端口号或服务。如果host是一个有效的主机名或ip地址,port是一个整数端口号或服务名,bash会尝试打开一个连接到该套接字的端口的TCP连接;

        第六种情况:(/dev/udp/host/port)

host表示主机名或者ip地址,port表示端口号或服务。如果host是一个有效的主机名或ip地址,port是一个整数端口号或服务名,bash会尝试打开一个连接到该套接字的端口的UDP连接;

        小贴士:进程向内核申请注册一个套接字,套接字需要用ip和端口号来标识。主机名最终会被解析成ip地址,进程注册的套接字可能是提供某一种服务。
 
?
        如果不能打开文件或者不能创建文件会导致重定向失败。
 
        重定向使用大于9的文件描述符应该小心谨慎,因为它们(大约9的文件描述符数字)可能会和shell的内部使用的文件描述符相冲突。

?
 

  • 输入重定向
    技术分享图片

[输入重定向]
        输入重定向会打开下面的word展开后所形成的文件名以备读取,并将其作为文件描述符n,如果没有指定n,则将word作为标准输入,之前的标准输入如果来自于键盘,现在省略了n,就表示word展开头的结果作为标准输入的内容。
标准格式为:

[n]<word 
例如cat < `echo /etc/crontab`,后边的 `echo /etc/crontab`是bash的展开特性中的一种,
是command substitution(命令替换),替换后的结果作为cat程序的标准输入。
cat发起一个进程,从后边的命令替换后的结果中读入内容,由CPU调度,加载到内存中。

 

  • 输出重定向
    技术分享图片

[输出重定向]
        输出重定向会打开word展开后的文件以备写入,并将其作为文件描述符n。如果没有指定n,则将其作为标准输出(文件描述符为1)。如果文件不存在,会先创建这个文件。如果文件存在,就会将文件中原先的内容清空(通过文件偏移量指针来实现,具体可以参考unix高级环境变量编程)。

 ?

  • 追加输出重定向
    技术分享图片
    [追加输出重定向]
            这种类型的输出重定向,word展开后形成的文件,以备追加使用,并将其作为文件描述符n。如果没有指定n,则将其作为标准输出(文件描述符1)。同样,文件不存在会创建,存在,则直接把文件偏移量指针指向文件的末尾,然后追加写入内容。

 ?

  • 标准输出和标准错误
    技术分享图片
    [标准输出重定向和标准错误重定向]
            这种结构允许把标准输出(文件描述符为1)和标准错误输出(文件描述为2)一起写入word扩展后的文件中。有以下几种形式可以表示:
&>word 
>&word
>word 2>&1

 ?

  • 追加标准输出和标准错误
    技术分享图片
    [追加标准输出重定向和追加标准错误重定向]
            这种结构允许把标准输出(文件描述符为1)和标准错误输出(文件描述符为2)一起追加写入word展开后的文件中。有以下两种格式:
&>>word
>>word 2>&1

 ?

  • 此处创建文档的使用(Here Documents)以及此处字符串(Here Strings)
    技术分享图片
    [此处文档]
            这种类型的重定向结构让shell从当前文本源中读取输入,直到遇到其中一行只包含word(这个word是我们自己定义的,通常为EOF(end of file),这一行word的前面不能有空白字符)。所有被读取的行都被当作命令的标准输入。格式如下:
    <<[-]word
        here-document
    delimiter 

    #转换一下就成了我们经常看见的一种格式:
    <<EOF
        documents
        ...
    EOF

        word这个标签不会进行参数展开,命令替换,算术展开,文件名展开。简单来说,标签部分,你即使用到了一个表达式,比如1+2,那么,你结束也要用1+2,即使有涉及展部分,也不会做bash的展开处理。
        还有两种情况要说明:
        第一种就是,如果用单引号"‘"或者双引号‘"‘,把word引用起来了,这种表示,我here-document的部分中如果即使有涉及 参数展开,命令替换,算术展开等都不会被bash展开处理。
        第二种就是,如果没有用引号(双引号和单引号都行)引起word,表示here-document中的部分内容会被bash的参数展开,命令替换,算术展开所匹配且会做对应的展开处理,不过换行符 不会被处理,而且如果你要显式特殊字符(,$,`)的本身,要用字符""做转移处理。简单看看下面三个例子:

示例1: 这个表示我标签word不会被bash展开处理的示例,$UID不会被替换成0或者其他用户id编号(结果自己分析,我不贴来)
cat <<$UID
123
hello,world
$UID
`$HOSTNAME`
`pwd`
$date


\`
$
$UID 

示例2:    这个表示我标签word用引号引起来,看看here-documents部分的解析情况(结果自己分析,我不贴来)
cat <<"EOF"
123
hello,world
$UID
`$HOSTNAME`
`pwd`
$date


\`
$
EOF

示例3:这个表示我标签word没有用引号引起来,看看here-documents部分的解析情况(结果自己分析,我不贴来)
cat <<EOF
123
hello,world
$UID
`$HOSTNAME`
`pwd`
$date


\`
$
EOF 

[此处字符串(即插即用字符串)]
? ? ?Here Strings这种形式是此处文档的一种变种,形式如下:

<<<word

? ? ?word支持bash的展开特性,然后作为命令的标准输入。例子:

wc -w <<< `date`
wc -w <<< "this is a strings"

?

技术分享图片

  • 对文件描述符的操作(复制,移动,读写)
            操作文件描述符,打开分为三种,形式如下:
[n]<&word和[n]>&word属于一类;(复制文件描述符)
[n]<&digit-和 [n]>&digit-属于一类;(移动关闭文件描述符)
[n]<>word 自成一类;(读写文件描述符)

 
 
[复制文件描述符]
格式1 : [n]<&word #为了好理解,写出几个符合这种格式的,2<&1,<&,<&2,1<&-等

        这种格式,表示用于复制输入文件描述符。如果word做展开后的结果包含一个或多个数字,则有n表示的文件描述符是后边数字表示的文件描述符的复制,如果word展开后数字代表的文件描述符不能作为打开一个输入的文件的文件描述符,冲顶会就会报错。简单来说,word如果是一个数字,如果这个数字对应的文件不能作为输入流,就是里面的东西读不到,就会报错。如果word展开后的结果值是一个符号"-",表示文件描述符n将会被关闭。如果n生路了,表示使用标准输入(文件描述符为0)。
 
格式2:[n]>&word
        这种格式,表示复制输出文件描述符。如果省略n表示,标准输出(文件描述符1)。如果word做展开后对应是一个数字且这个数字对应的文件描述符指定的文件不能被输出,简单来说数据流不能往这个文件中写,重定向就会报错。如果n生路了,且word展开后并不是一个数字, >& tring,表示标准输出和标准错误都被重定向到后边的文件中。之前有讲过这种情况。

 

[移动文件描述符]
格式1:[n]<&digit-
        移动digit这个数字指定的文件描述符内容到文件描述符n或者移动到标准输入(文件描述符0,n省略的情况下)。digit指定的文件描述符将会被关闭。
 
格式2:[n]>&digit-
        移动digit这个数字指定的文件描述符内容到文件描述符n或移动到标准输出(文件描述符1,n省略的情况下)。digit指定的文件描述符将会被关闭。

 
[打开文件描述符并读和写]
        以读写的方式打开word展开后的文件,并将文件描述符n重定向到该文件,如果n不指定,表示标准输入。

?

1.2、文件描述符和流的概念

本小结摘自《Unix高级环境变量编程》第三版的第三章的3.2小结部分以及第五章节的5.2小结部分
 
        通过以前的博客,我们有了基本文件系统的分层以及文件的认知。说到文件,我们不得不提一下一个叫做文件描述符的东西。对于操作系统内核而言,所有打开的文件都通过文件描述符来引用。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。按照惯例,Unix系统shell把文件描述符0与进程的标准输入关联,文件描述符1与标准输出关联,文件描述符2与标准错误关联。
 
        我们也之前讲过,操作系统抽象出了两层功能,一个是底层的系统调用接口,另外一个是叫做库(函数)的东西,而在我们的库函数中,有一类叫做标准I/O库的东西,这个库是由ISO C标准进行解释说明的。所有的I/O函数都是围绕文件描述符的。当打开一个文件时,即返回一个文件描述符,然后该文件描述符就用于后续的I/O操作。而对于标准I/O库,它们的操作是围绕流(stream)进行的。当用标准的I/O库打开或创建一个文件时,我们已经使一个流与一个文件相关联。

 
 

1.3、IO重定向概念

?        在第一小节,我花了过多的篇幅去翻译man手册中关于linux 的bash重定向部分,我是为了给自己做一个标记,方便下次自己学习查阅使用。如果没有心思或时间,可以直接跳过第一小节的部分,因为我是从这一部分正式介绍这篇博文的主题。
        首先来思考一个问题,何为IO?我们之前的博文中已经大体介绍过计算机的五大基本部件,分别是运算器控制器存储器输入设备输出设备,其中运算器和控制器集成于CPU这个硬件中来,存储器主要以内存为主,包括持久性存储设备硬盘,输入设备主要是键盘,鼠标,扫描器等,输出设备主要是显示器,打印机。这五大基本部件,是为了更好的让计算机来完成某些工作,这些工作的过程中就涉及到这些设备的IO操作。
        抽象概念,程序是由指令+数据组成。指令通常由程序来提供,但是数据却不单单如此。大部分的程序中的数据,可以有多种来源,程序内部自己赋值变量以及通过文件来提供这部分数据。程序本身也得有输入和输出(IO,Input,Output),这不过这部分被操作系统底层功能更好的给简化来。
 
        可用的输入设备:文件(unix抽象来万物皆文件的概念),这个文件可以包括键盘设备、文件系统上的常规文件、网卡等;
        可用的输出设备:文件,这个文件可以包括显示器、文件系统上的常规文件、网卡等;
 
        程序的数据流有三种:输入数据流(不显式指明的输入流叫标准输入,字符简写为stdin,默认的标准输入设备为键盘);输出数据流(不显式指定指明的时候为标准输出,字符简写为stdout,默认的标准输出设备为显示器);错误输出数据流(不显示指定指明的时候为标准错误,字符简写为stderr,默认的标准错误输出设备也是显式器),如下表所示:

索引编号 名字 英文简写 默认的设备
1 标准输入 stdin 键盘
2 标准输出 stdout 显式器
3 标准错误 stderr 显示器

 
        文件描述符(fd,file descriptor)-unix的概念相当于windows的文件句柄;人更倾向于识别的是名字,计算机识别的是二进制,为了更好的让系统完成工作,操作系统,把文件系统的文件以及其他抽象出来的文件都用不同标识的数字来表示,这些数字有一个统称的概念,叫做文件描述符。下表针对上述提到的三种标准流给出默认的标准流的文件描述符:

索引编号 名字 英文简写 文件描述符
1 标准输入 stdin 0
2 标准输出 stdout 1
3 标准错误 stderr 2

?

 
基础IO重定向(不含有复杂的)如下表所示:

索引编号 重定向名字 符号表示 说明 特点
1 输出重定向 > set -C ,会导致覆盖内容失败(前提是文件已存在才会失败),set +C关闭。 覆盖写入 ,不存在文件先创建再写入
2 强制输出重定向 > set -C的时候也会强制写入,危险 覆盖写入,不存在文件先创建再写入
3 追加输出重定向 >> 追加写入,不会覆盖原有文件内容,不存在文件先创建然后再写入
4 错误输出重定向 2> 覆盖写入错误信息,文件不存在先创建文件,然后再写入错误信息
5 追加错误输出重定向 2>> 追加写入错误信息,文件不存在先创建,然后再写入错误信息
6 合并输出流和错误输出流,覆盖 &>或>&或COMMAND> FILE 2>&1 set -C,会导致覆盖内容写入失败(前提是文件已存在才会失败),set +C关闭 把标准输出和标准错误信息都追加写入文件中
7 合并输出流和错误输出流,强制覆盖 COMMAND> FILE 2>&1 set -C的时候回强制写入,及时文件存在,危险 把标准输出和标准错误信息都强制写入文件中,文件存在也会强制写入,不管安全选项。文件不存在会先创建
8 合并输出流和错误输出流,追加 &>>或COMMAND>> FILE 2>&1 把标准输出和标准错误信息都覆盖写入文件中,文件不存在会先创建然后覆盖写入
9 输入重定向 <
10 此处创建文档(Here Documents) < <

 
?
 
网上的一个总结,觉得比较好,列出来:

 I/O REDIRECTORS
    cmd1 | cmd2   use stdout of cmd1 as stdin for cmd2,cmd1的标准输出作为cmd2的标准输入;
         > file   save stdout to file ,把标准输出重定向到file文件;
         < file   use file as stdin ,把file当做标准输入重定向;
        >> file   append stdout to file (create file if nonexistent) ,追加标准输出重定向到file(如果file不存在,先创建);
        >| file   force stdout to file (even if noclobber is set) 强制把标准输出重定向到file(即使noclobber启动的);
       n>| file   force stdout to file from descriptor n (even...) 把文件描述符n的标准输出强制定向到file;
        <> file   use file as both stdin and stdout (process in place) ,把文件当做标准输入和标准输出;
       n<> file   use as both stdin and stdout for file descriptor n
        << label  use here-document (within scripts specifies batch input) 此处文档;
        n> file   direct file descriptor n to file  把文件描述符n给文件file;
        n< file   take file descriptor n from file 把文件描述符n作为输入;
       n>> file   append descriptor n to file (or create file for n) 追加文件描述符n到文件;
       n>&        duplicate stdout to file descriptor n  ,复制标准输出给文件描述符n;
       n<&        duplicate stdin from file descriptor n  从文件描述符n中复制标准输入;
       n>&m       make file descriptor n a copy of the stdout fd 从标准输出文件描述符中复制一份副本到文件描述符n;
       n<&m       make file descriptor n a copy of the stdin  fd,从标准输入文件描述符中复制一份副本到文件描述符n;
        >&file    send stdout and stderror to file 把标准输出和标准错误都发送给文件file;
       <&-        close stdin  ,关闭标准输入;
       >&-        close stdout,关闭标准输出;
      n<&-        close input  from file descriptor n,关闭来自于文件描述符n的输入;
      n>&-        close output from file descriptor n,关闭来自于文件描述符n的输出;

学习必看的重定向相关的文章

 
官方的一张图:
技术分享图片

  • 管道
    管道主要是连接程序,实现将前一个命令的输出直接定向后一个程序当作输入数据流。大体格式如下:
COMMAND1 | COMMAND2 | COMMAND3 | ...

1.4、几个涉及重定向相关的命令

  • cat 命令
我们之前针对cat命令的选项进行过基本说明,我们这里直接引用;
Concatenate FILE(s), or standard input, to standard output.
cat的功能就是把多个文件的内容进行合并并输出到标准输出(默认显示器),其实当我们省略
cat的FILES的时候,cat是能够从标准输入(默认键盘)接收内容的。

结构:
cat [OPTION]... [FILE]...

我们来看几个简单的案例。

1. 案例1:直接从默认标准输入中接受内容并写入默认标准输出
[[email protected] ~]# cat
xiaochang,nihaoma?
xiaochang,nihaoma?
123
123
^C

2. 案例2:从文件中输入内容给cat并显示到默认标准输出,这样通过重定向改变来cat程序的输入流
[[email protected] ~]# cat < /etc/fstab

#
# /etc/fstab
# Created by anaconda on Tue Aug  7 21:36:15 2018
#
# Accessible filesystems, by reference, are maintained under ‘/dev/disk‘
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=122ecc46-d280-4282-a800-d6e112227b60 /                       xfs     defaults        0 0
UUID=fd2db043-73ce-4418-bf16-93ffaceb7a6f /boot                   xfs     defaults        0 0
UUID=79f6ca5b-2ef2-43cc-b100-d63555b1b547 swap                    swap    defaults        0 0
  • tr命令:
tr - translate or delete characters,翻译或删除字符

tr [OPTION]... SET1 [SET2]

把输入的数据中的字符,凡是在SET1定义范围内出现的,都对位转换为SET2出现的字符。
默认不直接操作源文件,如果输入的流来自原某个具体文件系统的文件的话。

案例一:把小写字母转大写字母
[[email protected] ~]# echo {a..z}|tr [[:lower:]] [[:upper:]]
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
[[email protected] ~]# echo {a..z}|tr [a-z] [A-Z]
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

[[email protected] ~]# tr [[:lower:]] [[:upper:]] < /etc/passwd
ROOT:X:0:0:ROOT:/ROOT:/BIN/BASH
BIN:X:1:1:BIN:/BIN:/SBIN/NOLOGIN
DAEMON:X:2:2:DAEMON:/SBIN:/SBIN/NOLOGIN
ADM:X:3:4:ADM:/VAR/ADM:/SBIN/NOLOGIN
LP:X:4:7:LP:/VAR/SPOOL/LPD:/SBIN/NOLOGIN
....

案例二:修剪字符串
[[email protected] ~]# echo {a..z}|tr -d [a-j]
          k l m n o p q r s t u v w x y z
[[email protected] ~]#                 
  • tee命令:
tee - read from standard input and write to standard output and files
从标准输入从读取数据,并把其写入标准输出和文件中。
结构:
tee [OPTION]... [FILE]...

选项:
-a,--append:追加内容到给定的FILEs中,不覆盖写;

案例:
[[email protected] ~]# head -4 /etc/passwd | tr [[:lower:]] [[:upper:]] | tee -a /tmp/test/issue 
ROOT:X:0:0:ROOT:/ROOT:/BIN/BASH
BIN:X:1:1:BIN:/BIN:/SBIN/NOLOGIN
DAEMON:X:2:2:DAEMON:/SBIN:/SBIN/NOLOGIN
ADM:X:3:4:ADM:/VAR/ADM:/SBIN/NOLOGIN
[[email protected] ~]# cat /tmp/test/issue
S
Kernel 
 on an m

ROOT:X:0:0:ROOT:/ROOT:/BIN/BASH
BIN:X:1:1:BIN:/BIN:/SBIN/NOLOGIN
DAEMON:X:2:2:DAEMON:/SBIN:/SBIN/NOLOGIN
ADM:X:3:4:ADM:/VAR/ADM:/SBIN/NOLOGIN

 

1.5、实际案例说明

  • 改变默认标准输入流
利用输入重定向符号"<"来读取/etc/passwd文件的最后2行给cat程序:
[[email protected] ~]# tail -2 /etc/passwd | cat
gentoo:x:4001:4001::/var/tmp/gentoo:/bin/csh
fedora:x:4002:4002:Fedora Core:/home/fedora:/bin/tcsh
分析:管道把前边部分的输出流给管道后边部分的输入流,我们就说tail -2 /etc/passwd这一部分
的程序的标准输出重定向到了管道后边的程序,cat的标准输入被重定向到流管道前边的
部分(即cat程序的标准输入来自于管道前边部分的内容)。
  • 改变默认标准输出输出流
把ls 查看/etc/pam.d目录下的长格式显示的内容写入到/var/tmp目录下的一个叫test(文件默认不存在)的文件中
[[email protected] ~]# ls -l /etc/pam.d/ > /var/tmp/test
[[email protected] ~]# ls -l /etc/pam.d/ 1> /var/tmp/test
[[email protected] ~]# cat /var/tmp/test
total 100
-rw-r--r--. 1 root root 192 Mar  6  2015 chfn
-rw-r--r--. 1 root root 192 Mar  6  2015 chsh
-rw-r--r--. 1 root root 232 Mar  6  2015 config-util
-rw-r--r--. 1 root root 293 Jul 30  2014 crond
... #此处省略

分析:
利用输出重定向符号">"来改变程序ls 到默认标准输出的流到文件/var/tmp/test中。因为标准
输出文件描述符为1,省略或者显式指定(即>或1>)都可以。

启用noclobber,然后再次测试覆盖写:
set -o noclobber或set -C 表示启用noclobber特性;(当前shell级别)
set +o noclobber 或set +C 表示关闭noclobber特性;(当前shell级别)
[[email protected] ~]# ls /var/tmp/test
/var/tmp/test
[[email protected] ~]# cat /var/tmp/test
ls: cannot access hello: No such file or directory
-rw-------. 1 root root 263 Sep  5 06:12 .lesshst
[[email protected] ~]# echo 123 > /var/tmp/test
[[email protected] ~]# cat /var/tmp/test
123
[[email protected] ~]# set -o noclobber
[[email protected] ~]# echo 145 > /var/tmp/test
-bash: /var/tmp/test: cannot overwrite existing file
[[email protected] ~]# ls -l /var/tmp/test10
ls: cannot access /var/tmp/test10: No such file or directory
[[email protected] ~]# echo 145 > /var/tmp/test10
[[email protected] ~]# cat /var/tmp/test10
145
[[email protected] ~]# echo hello > /var/tmp/test10
-bash: /var/tmp/test10: cannot overwrite existing file
[[email protected] ~]# echo hello >| /var/tmp/test10
[[email protected] ~]# cat /var/tmp/test10
hello
首先/var/tmp/test文件存在,我在安全选项未开启前,直接覆盖,可以写入。
开启选项后,直接覆盖写入会报错,而我写入另外一个不存在的文件/var/tmp/test10
就可以写入,然后我利用强制覆盖写(>|)可以写入。
  • 改变默认标准错误流
[[email protected] ~]# ls -l hello
ls: cannot access hello: No such file or directory
[[email protected] ~]# ls -l hello .lesshst 2>/var/tmp/test 
-rw-------. 1 root root 263 Sep  5 06:12 .lesshst
[[email protected] ~]# cat /var/tmp/test 
ls: cannot access hello: No such file or directory

分析:因为默认输出重定向符号">"前边部分的文件描述符数字如果省略表示标准输出,
如果改变标准错误的流的重定向,需要显式指明文件描述符 2。
  • 把标准错误流和标准输出流都改变
[[email protected] ~]# ls -l hello .lesshst &>/var/tmp/test 
[[email protected] ~]# cat /var/tmp/test
ls: cannot access hello: No such file or directory
-rw-------. 1 root root 263 Sep  5 06:12 .lesshst
[[email protected] ~]# ls -l hello .lesshst >& /var/tmp/test1
[[email protected] ~]# cat /var/tmp/test1
ls: cannot access hello: No such file or directory
-rw-------. 1 root root 263 Sep  5 06:12 .lesshst
[[email protected] ~]# ls -l hello .lesshst > /var/tmp/test2 2>&1
[[email protected] ~]# cat /var/tmp/test2 
ls: cannot access hello: No such file or directory
-rw-------. 1 root root 263 Sep  5 06:12 .lesshst
三种形式,把标准错误和标准输出的流都覆盖写入某文件。如下格式:
COMMAND &> /PATH/TO/SOMEDIR/FILE
COMMAND >& /PATH/TO/SOMEDIR/FILE
COMMAND > /PATH/TO/SOMEDIR/FILE 2>&1
  • 自定义文件描述符来完成流定向工作
[[email protected] ~]# cat <<EOF>/root/test.log
> hello,world
> i am yanhui
> nice to meet you
> nice to meet you too
> ok,bye.
> EOF
[[email protected] ~]# exec 6</root/test.log 
[[email protected] ~]# cat <&6
hello,world
i am yanhui
nice to meet you
nice to meet you too
ok,bye.
[[email protected] ~]# ls -l /dev/fd/6
lr-x------. 1 root root 64 Sep  9 05:43 /dev/fd/6 -> /root/test.log
[[email protected] ~]# exec 6<&-
[[email protected] ~]# ls -l /dev/fd/6
ls: cannot access /dev/fd/6: No such file or directory

自定义的fd(file descriptor)的数字范围0~9,0,1,2默认都是占用的,使用3到9的时候要注意是否占用。可以查看/dev/fd目录:
ls -l /dev/fd/

上面的示例,我定义了一个文件描述符6,把它与/root/test.log文件关联,这样,可以使用
文件描述符6的重定向的输入流,来自于/root/test.log,非默认标准输入流,使用的时候,
记得要带上文件描述符6,最后我们关闭流自定义的文件描述符。

[[email protected] ~]# exec 7>/root/yanhui.log
[[email protected] ~]# cat /root/yanhui.log
[[email protected] ~]# echo ‘123‘
123
[[email protected] ~]# echo ‘123‘ >&7
[[email protected] ~]# echo ‘234‘ >&7
[[email protected] ~]# ls -l /dev/fd/7
l-wx------. 1 root root 64 Sep  9 05:50 /dev/fd/7 -> /root/yanhui.log
[[email protected] ~]# exec 7>&-
[[email protected] ~]# ls -l /dev/fd/7
ls: cannot access /dev/fd/7: No such file or directory
[[email protected] ~]# cat /root/yanhui.log 
123
234

上面的例子中,我自定义一个文件描述符7,把该文件描述符的输出流重定向到文件/root/yanhui.log中,
值得注意的时,我们自定义的文件描述符,流数据,不是覆盖写,而是追加写。

小贴士:

自定义文件描述符,理解起来可能会有些难,如果新手入门,可以暂时忽略掉这一部分。
目前我工作中用到自定义文件描述符有两个应用用途:
检查ssh远程登录公钥文件的一致性;
利用自定义文件描述符来结合mysqldump逻辑备份,实现批量任务备份。
  • 此处文档(Here Documents)
结构形式三种:(以cat程序为例,EOF标签和$UID标签为例)
第一种===>标签为特殊字符,验证其不做bash的扩展(expansion)
标准格式:
<<[-]word
here-documents
...
delimiter
=============================
cat <<$UID
123
hello,world
0
$UID
============================
[[email protected] ~]# cat <<$UID
> 123
> hello,world
> 0
> $UID
123
hello,world
0

第二种===>验证标签(定界符匹配串)引起来
标准格式:
<<[-]["|‘]word["|‘]
here-documents
...
delimiter

[[email protected] ~]# cat <<-"EOF"
hello,world
$
$date
`
`date`
how old are you?
EOF

hello,world
$
$date
`
`date`
how old are you?

当我用引号(单引号或双引号)把标签(EOF)引起来的时候,中间Here-Documents部分的内容都没有
进行bash的expansion,而且定界符结束匹配的是标签没有引号的部分即EOF。

第三种===>验证标签(定界符匹配串)没引起来
[[email protected] ~]# cat <<-EOF
hello,world
$
$date
`
`date`
how old are you?
EOF

hello,world
$

`
Sun Sep  9 05:16:37 CST 2018
how old are you?

可以看到,没有引起(单引号或双引号都可以做引用)标签部分,特殊字符会被解析(准确点说叫做
会做部分类型的bash的扩展,expansion)
  • 此处文档的变体(Here Strings)
标准格式:
<<<word
其中word部分支持bash的expansion;

[[email protected] ~]# cat<<<$HOSTNAME
localhost.localdomain
[[email protected] ~]# echo $HOSTNAME
localhost.localdomain

用cat程序来查看主机名,通过环境变量。

以上是关于linux下的IO重定向与管道相关的知识简析的主要内容,如果未能解决你的问题,请参考以下文章

Linux中IO重定向和管道

谈谈Linux下的数据流重定向和管道命令

linux基础篇-11,IO重定向和管道

Linux基础入门--IO重定向及管道

linux基础05-管道及IO重定向

Linux学习笔记 第六章标准IO和管道