我在刚开始接触IO重定向时,经历了由懂到不懂然后了然的过程,当然现在的我也不可能说对于IO重定向就彻底懂了,只是在解决一些问题或实现某些结果时不会因为IO重定向的问题而迷惑了。

   


    什么叫IO重定向?

为了解释这个问题,我们要先明白什么叫IO,什么叫做程序的数据流。

     什么叫IO?

在Linux或计算机领域IO指的是信息的输入和输出。

在Linux中可用于输入的设备:文件(Linux中一切皆文件)

具体包括:键盘设备、文件系统上的常规文件、网卡等。

在Linux中用于输出的设备:文件

具体包括:显示器、文件系统上的常规文件、网卡等。

    什么叫做数据流?

我们通常描述的信息本质上来说都是数据,因为信息处于动态,我们就将信息或者命令等概念称作流动的数据也就是数据流。

数据流分为输入流(InputStream)和输出流(OutputStream)两类。输入流只能读不能写,而输出流只能写不能读。通常程序中使用输入流读出数据,输出流写入数据,就好像数据流入到程序并从程序中流出。采用数据流使程序的输入输出操作独立与相关设备。输入流可从键盘或文件中获得数据,输出流可向显示器、打印机或文件中传输数据。

    在Linux中,程序的数据流有三种:

输入的数据流:←标准输入 STDIN 常用设备:键盘

输出的数据流:→标准输出 STDOUT 常用设备:终端

错误输出流: →错误输出 STDERR 常用设备:终端

在Linux中打开一个文件,在该文件被加载完成后对于系统来说需要用一个数字表示,这个数字叫做fd:file descriptor 文件描述符。

因为Linux系统中一切皆文件,所以对应的标准输入设备,标准输出设备,错误输出设备都有其对应的fd

    标准输入:0

    标准输出:1

    错误输出:2

    IO重定向的意思就是将系统原本默认的输入输出的路径重新定向到其他文件

我知道解释到这里还是会很困惑,别着急,这个概念需要通过一定量的实验来说明。



    输出重定向

    其常用 >>>>|这三种表示

    >:覆盖输出  >>:追加输出  >|:强行覆盖

因为使用>不当会对原有的数据造成很大的影响,所以有时候我们会手动设定关闭>的特性。其命令为:

set -C禁止覆盖输出重定功能

set +C为开启覆盖输出重定向功能

不过上述命令对“>|”无效

下面以具体实验来说明上面内容

实验环境:在/test/目录下

[root@localhost test]# ll
总用量 0
[root@localhost test]# echo $(date) > t1
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 43 8月   2 18:15 t1
[root@localhost test]# cat t1
2016年 08月 02日 星期二 18:15:28 CST
[root@localhost test]# echo test > t1
[root@localhost test]# cat t1
test
[root@localhost test]# echo $(who) >> t1
[root@localhost test]# cat t1
test
root :0 2016-08-02 10:12 (:0) root pts/0 2016-08-02 10:12 (192.168.85.1) root pts/1 2016-08-02 10:19 (192.168.85.1)
[root@localhost test]# set -C
[root@localhost test]# echo 111 > t1
-bash: t1: 无法覆盖已存在的文件
[root@localhost test]# echo 111 >| t1
[root@localhost test]# cat t1
111

上面的结果显示了>、>>、>|三者的各自功能

相信看到这里对于输入重定向会有了进一步的认识。



    错误输出流重定向 

    2> 2>>

[root@localhost test]# echoo 1 > t2
bash: echoo: 未找到命令...

当命令错误时,shell会返回错误信息,若我们想将错误信息存到某个文件则可按如下操作

[root@localhost test]# echoo 1 2> t2
-bash: t2: 无法覆盖已存在的文件
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 4 8月   2 18:18 t1
-rw-r--r--. 1 root root 0 8月   2 18:20 t2
[root@localhost test]# echoo 1 2> t3
[root@localhost test]# cat t3
bash: echoo: 未找到命令...
[root@localhost test]# echop wqew 2>>t3
[root@localhost test]# cat t3
bash: echoo: 未找到命令...
相似命令是: 'echo'
bash: echop: 未找到命令...

错误重定向也支持追加。

如果我们不管命令对错都想记录那么可以使用如下格式实现:

1、&>&>>

[root@localhost test]# ech2 &>> t4
[root@localhost test]# echo right &>> t4
[root@localhost test]# cat t4
bash: ech2: 未找到命令...
right

2、command > /path/somewhere ... 2>&1 ;command >> /path/somewhere ... 2>&1 第二种只能为此种格式,不管前面是追加还是覆盖后面均为2>&1。2>>&1为错误格式,使用后在centos7.2下会报错。

[root@localhost test]# ech2 >> t5 2>&1      
[root@localhost test]# echo right >> t5 2>&1
[root@localhost test]# cat t5
bash: ech2: 未找到命令...
right

注:输出重定向的对象可以是各种文件,但不能将其定向至命令的输入,要想实现这个功能则需要用管道。这里大家可能有点迷,下面以例子说明

[root@localhost test]# who > tr a-z A-Z > t6
[root@localhost test]# cat t6
root     pts/0        2016-08-02 10:12 (192.168.85.1)
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 54 8月   2 18:53 t6
-rw-r--r--. 1 root root  0 8月   2 18:53 tr

我们将who显示的命令重定向至tr命令的输入,之后将内容小写转换为大写后再重定向至t6文件。我们想实现的是这个结果,但是并没有实现,反而在过程中出现了tr这个文件。这是因为在第一层的重定向中,默认将tr当做文件名,但是奇怪的是tr内什么都没有,所以这种错误的理解尽早摆脱。

[root@localhost test]# cat tr 
[root@localhost test]#

我们可以使用管道完美解决上面的问题。

[root@localhost test]# who | tr a-z A-Z > t6
[root@localhost test]# cat t6
ROOT     :0           2016-08-02 10:12 (:0)
ROOT     PTS/0        2016-08-02 10:12 (192.168.85.1)
ROOT     PTS/1        2016-08-02 10:19 (192.168.85.1)

什么是管道,我相信通过上面的例子大家应该对它有了个初步的认知。


管道:连接程序,实现将前一个命令的输出直接定向后一个程序当做输入数据流。

其常用表达格式如下:  COMMAND1 | COMMAND2 | COMMAND3 |...

它是输出重定向的补充与加强,其本质还是类属于输出重定向。

既然说道管道,这里再多补充点

常用于管道的命令:

tee

tee - read from standard input and write to standard output and files

      显示标准输入的结果并将其写入文件

    tee的使用格式:

    命令1 | tee 文件名| 命令2

把命令1的STDOUT保存在文件名中,然后管道输入给命令2

作用:

    保存不同阶段的输出

    复杂管道的故障排除

    同时查看和记录输出

下面通过一个简单的例子说明tee的具体作用

[root@localhost test]# who | tee t2 | tr a-z A-Z
ROOT     :0           2016-08-02 10:12 (:0)
ROOT     PTS/0        2016-08-02 10:12 (192.168.85.1)
ROOT     PTS/1        2016-08-02 10:19 (192.168.85.1)
[root@localhost test]# cat t2
root     :0           2016-08-02 10:12 (:0)
root     pts/0        2016-08-02 10:12 (192.168.85.1)
root     pts/1        2016-08-02 10:19 (192.168.85.1)

tee可以将前一个的结果记录在t2文件内,同时显示后一个命令的结果。




    输入重定向

     < 单行模式、 << 多行模式

要了解输入重定向,我们得先了解一个命令

tr

 tr - translate or delete characters 转换或删除字符

其表达格式如下:

    tr[OPTION]... SET1 [SET2]

选项:

    -c或——complerment:取字符集的补集

    -d或——delete:删除所有属于第一字符集的字符

    -s或—squeeze-repeats:把连续重复的字符以单独一个字符表示

下面我们通过一个例子简要了解下tr的作用

[root@localhost test]# cat t1
root     :0           2016-08-02 10:12 (:0)
root     pts/0        2016-08-02 10:12 (192.168.85.1)
root     pts/1        2016-08-02 10:19 (192.168.85.1)
[root@localhost test]# tr a-z A-Z < t1
ROOT     :0           2016-08-02 10:12 (:0)
ROOT     PTS/0        2016-08-02 10:12 (192.168.85.1)
ROOT     PTS/1        2016-08-02 10:19 (192.168.85.1)

t1是上面tee存储的who内容,我们通过输入重定向将其作为tr的输入,并由小写转换为大写。

输入重定向就是将原来用键盘输入的内容通过其他文本内容或文件内容代替。

我想大家对于<<多行重定向有些不理解,这里再对多行重定向在以例子说明一下。

[root@localhost ~]# tr a-z A-Z << end
> morning,boys
> today is another day
> please be happy
> end
MORNING,BOYS
TODAY IS ANOTHER DAY
PLEASE BE HAPPY

多行输入重定向后面需要接规定的结尾字符串,通常使用eof或end为结尾标示符。

多行输入重定向需要手动键入内容。单行输入重定向则需要跟文件或前一个命令的输出结果。



我知道看到这里依然会有点懵,下面通过9个实例来综合使用标准输出输入及管道命令

实验环境CentOS7.2


1、将/etc/issue文件中的内容转换为大写后保存至/test/issue#文件中

[root@localhost test]# cat /etc/issue | tr a-z A-Z > /test/issue1
[root@localhost test]# echo $(tr a-z A-Z < /etc/issue) > /test/issue2
[root@localhost test]# cat issue1
\S
KERNEL \R ON AN \M
[root@localhost test]# cat issue2
\S KERNEL \R ON AN \M

上面两种方式均可以实现要求,只不过输出格式略有不同,这根命令有关。在没有进一步的限制下,这两种表达方式均可。第一种使用管道,输出重定向,第二种使用命令引用,输入重定向及输出重定向。


2、将当前系统登录用户的信息转换为大写后保存至/test/t2文件中

[root@localhost test]# who | tr a-z A-Z > /test/t2
[root@localhost test]# cat t2
ROOT     :0           2016-08-02 10:12 (:0)
ROOT     PTS/0        2016-08-02 10:12 (192.168.85.1)
ROOT     PTS/1        2016-08-02 10:19 (192.168.85.1)


3、一个linux用户给root发邮件,要求邮件标题为”help”,邮件正文如下:

Hello, I am 用户名,the system version is here,pleasehelp me to check it ,thanks!

操作系统版本信息

我们使用用户gentoo

[root@localhost ~]# su gentoo
[gentoo@localhost /root]$ whoami
gentoo
[gentoo@localhost /root]$ cd /home/gentoo/
[gentoo@localhost gentoo]$ echo -e "Hello,I am `whoami`\nThe system version is here,please help me to check it ,thanks.\nThe version is `cat /etc/redhat-release`" | mail -s "help" root   
[gentoo@localhost gentoo]$ exit
exit
您在 /var/spool/mail/root 中有新邮件
[root@localhost ~]# mail
Heirloom Mail version 12.5 7/5/10.  Type ? for help.
"/var/spool/mail/root": 3 messages 1 new
    1 gentoo@localhost.loc  Tue Aug  2 09:38  21/731   "help"
    2 root                  Tue Aug  2 09:44  21/741   "help"
>N  3 gentoo@localhost.loc  Tue Aug  2 19:37  20/737   "help"
& 3
Message  3:
From gentoo@localhost.localdomain  Tue Aug  2 19:37:36 2016
Return-Path: <gentoo@localhost.localdomain>
X-Original-To: root
Delivered-To: root@localhost.localdomain
Date: Tue, 02 Aug 2016 19:37:36 +0800
To: root@localhost.localdomain
Subject: help
User-Agent: Heirloom mailx 12.5 7/5/10
Content-Type: text/plain; charset=us-ascii
From: gentoo@localhost.localdomain
Status: R
-e Hello,I am gentoo
The system version is here,please help me to check it ,thanks.
The version is CentOS Linux release 7.2.1511 (Core)

这里使用了命令替换来显示用户及版本信息,管道将第一个命令的输出递给下一个命令作为其输入。


4、将/root/下文件列表,显示成一行,并文件名之间用空格隔开

[root@localhost test]# ls -1 /root | tr '\n' ' '\n
anaconda-ks.cfg CST dead.letter file initial-setup-ks.cfg test.txt tr 公共 模板 视频 图片 文档 下载 音乐 桌面 [root@localhost test]#

上面命令已经满足我们的要求了,只不过不换行看着有些别扭,下面我们在完善下使其输出看着更舒服些。

[root@localhost test]# echo -e `ls -1 /root/ | tr '\n' ' '\n` 
anaconda-ks.cfg CST dead.letter file initial-setup-ks.cfg test.txt tr 公共 模板 视频 图片 文档 下载 音乐 桌面
[root@localhost test]#

使用echo的-e开启转义符使换行\n生效。


5、file1文件的内容为:”1 2 3 4 5 6 7 8 9 10” 计算出所有数字的总和

[root@localhost test]# cat > file1
1 2 3 4 5 6 7 8 9 10
^C
[root@localhost test]# cat file1 
1 2 3 4 5 6 7 8 9 10

先创建file1文件夹并输入题目要求内容。

[root@localhost test]# cat file1 | tr ' ' '+' | bc
55


6、处理字符串“xt.,l 1 jr#!$mn2 c*/fe3  uz4”,只保留其中的数字和空格

[root@localhost test]# echo 'xt.,l 1 jr#!$mn2 c*/fe3  uz4' | tr -cd '[:digit:][:space:]'
 1 2 3  4

这里使用了tr中的-c及-d补集跟删除,灵活使用补集概念可以更准确的帮助我们过滤不需要的信息。


7、将PATH变量每个目录显示在独立的一行

[root@localhost test]# echo ${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost test]# echo ${PATH} | tr ':' '\n'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/root/bin

这个题很简单只需要将PATH正确显示出来,使用tr将:替换为换行即可


8、删除指定文件的空行

这里我们创建一个含有很多空行的文本文件t9

[root@localhost test]# cat t9
q



ere
s



df
sd




sd
[root@localhost test]# cat t9 | tr -s '\n' 
q
ere
s
df
sd
sd

这里使用了tr的重复删除-s选项。


9、将文件中每个单词(字母)显示在独立的一行,并无空行

这里我们选择/etc/rc.d/rc0.d/K50netconsole文件,使用head查看下其文件前5行都什么内容

[root@localhost test]# head -5 /etc/rc.d/rc0.d/K50netconsole 
#!/bin/bash
# 
# netconsole    This loads the netconsole module with the configured parameters.
#
# chkconfig: - 50 50
[root@localhost test]# cat /etc/rc.d/rc0.d/K50netconsole | tr -cs '[:alpha:]' '\n' | head -5

bin
bash
netconsole
This

从结果来看我们实现了题目的要求,不过为什么第一行是空行,这是因为-s是将所有的空行压缩为一行,因此前面会留有一行空行。