Shell脚本攻略04-玩转文件描述符及重定向

概述

文件描述符是与文件输入、输出相关联的整数。它们用来跟踪已打开的文件。

最常见的文件描述符是stdin、 stdout和stderr。

我们甚至可以将某个文件描述符的内容重定向到另一个文件描述符中。

下面给出一些对文件描述符进行操作和重定向的例子。

理论知识

在编写脚本的时候会频繁使用标准输入(stdin)、标准输出(stdout)和标准错误(stderr)。

通过内容过滤将输出重定向到文件是我们平日里的基本任务之一。当命令输出文本时,这些输出文本有可能是错误信息,也可能是正常的(非错误的)输出信息。

单靠查看输出的文本本身,我们没法区分哪些是正常,哪些是错误。

不过可以通过文件描述符来解决这个问题,将那些与特定描述符关联的文本提取出来。文件描述符是与某个打开的文件或数据流相关联的整数。

文件描述符0、 1以及2是系统预留的。

  • 0 —— stdin(标准输入)。
  • 1 —— stdout(标准输出)。
  • 2 —— stderr(标准错误)。

实战演练

将输出文本重定向或保存到一个文件中

[root@entel1 Templates]# echo "Welcome to xiaogongjiang's blog 1" > temp.txt
[root@entel1 Templates]# cat temp.txt 
Welcome to xiaogongjiang's blog 1

这种方法通过截断文件的方式,将输出文本存储到文件temp.txt中。

也就是说在把echo命令的输出写入文件之前, temp.txt中的内容首先会被清空。


将文本追加到目标文件中

[root@entel1 Templates]# echo "Welcome to xiaogongjiang's blog 2" >> temp.txt
[root@entel1 Templates]# cat temp.txt 
Welcome to xiaogongjiang's blog 1
Welcome to xiaogongjiang's blog 2
[root@entel1 Templates]# 

标准错误以及如何对它重定向

当命令输出错误信息时, stderr信息就会被打印出来。

[root@entel1 Templates]# ls +
ls: cannot access +: No such file or directory

这里, +是一个非法参数,因此将返回错误信息

成功和不成功的命令

当一个命令发生错误并退回时,它会返回一个非0的退出状态;
而当命令成功完成后,它会返回数字0。退出状态可以从特殊变量$?中获得.

(在命令执行之后立刻运行echo $?,就可以打印出退出状态)。

[root@entel1 Templates]# cat cmdRW.sh 
#!/bin/bash
#判断命令是否执行成功
pwd
echo $?

if [ $? ==  0 ]
    then 
        echo RIGHT 
    else 
        echo WRONG
fi
[root@entel1 Templates]# ./cmdRW.sh 
/root/Templates
0
RIGHT
[root@entel1 Templates]# 

默认stderr打印到屏幕上

下面的命令会将stderr文本打印到屏幕上,而不是文件中(而且因为并没有stdout的输出,所以out.txt没有内容):

[root@entel1 Templates]# ls + > out.txt
ls: cannot access +: No such file or directory
[root@entel1 Templates]# cat out.txt 
[root@entel1 Templates]# 

stderr重定向到文本

下面的命令中,我们将stderr重定向到out.txt:

[root@entel1 Templates]# ls + 2> out.txt  #正常运行
[root@entel1 Templates]# cat out.txt 
ls: cannot access +: No such file or directory
[root@entel1 Templates]# 

将stderr单独重定向到一个文件,将stdout重定向到另一个文件

你可以将stderr单独重定向到一个文件,将stdout重定向到另一个文件:

[root@entel1 Templates]# ls + 1> 1.txt 2>2.txt
[root@entel1 Templates]# cat 2.txt 
ls: cannot access +: No such file or directory
[root@entel1 Templates]# ls 1>1.txt 2>2.txt #执行后,虽然没有错误输出2.txt的内容也会被截断
[root@entel1 Templates]# cat 1.txt 
1.txt
2.txt
bc.sh
cmdRW.sh
test.sh
variables.sh
[root@entel1 Templates]# cat 2.txt #内容被清空
[root@entel1 Templates]# 

stderr和stdout重定向到同一个文件中

还可以利用下面这个更好的方法将stderr转换成stdout,使得stderr和stdout
都被重定向到同一个文件中

$ cmd >output.txt   2>&1

或者这样:

$ cmd &> output.txt
[root@entel1 Templates]# pwd >file.txt 2>&1
[root@entel1 Templates]# cat file.txt 
/root/Templates
[root@entel1 Templates]# pwd+ >>file.txt 2>&1 #使用 >> 追加到file.txt中 
[root@entel1 Templates]# cat file.txt 
/root/Templates
-bash: pwd+: command not found
[root@entel1 Templates]# pwd >& output.txt 
[root@entel1 Templates]# cat output.txt 
/root/Templates
[root@entel1 Templates]# pwd2 >&output.txt 
[root@entel1 Templates]# cat output.txt 
-bash: pwd2: command not found

将stderr的输出重定向到 /dev/null

有时候,在输出中可能包含一些不必要的信息(比如调试信息)。如果你不想让终端中充斥着有关stderr的繁枝末节,那么你可以将stderr的输出重定向到 /dev/null

cmd 2>/dev/null

既可以将数据重定向到文件,还可以提供一份重定向数据的副本作为后续命令的stdin

当对如果对stderr或stdout进行重定向,被重定向的文本会传入文件。因为文本已经被重定向到文件中,也就没剩下什么东西可以通过管道(|)传给接下来的命令,而这些命令是通过stdin进行接收的。


但是有一个方法既可以将数据重定向到文件,还可以提供一份重定向数据的副本作为后续命令的stdin.这一切都可以使用tee来实现。

举个例子:要在终端中打印stdout,同时将它重定向到一个文件中,那么可以这样使用tee:

command | tee FILE1 FILE2
[root@entel1 Templates]# cat a.txt | tee output.txt | cat -n
     1  xiaogongjiang
[root@entel1 Templates]# cat output.txt 
xiaogongjiang
[root@entel1 Templates]# 

分析:在下面的代码中, tee命令接收到来自stdin的数据。它将stdout的一份副本写入文件out.txt,同时将另一份副本作为后续命令的stdin。命令cat -n将从stdin中接收到的每一行数据前加上行号并写入stdout.

[root@entel1 Templates]# catxx  a.txt | tee output.txt | cat -n
-bash: catxx: command not found
[root@entel1 Templates]# cat output.txt 
[root@entel1 Templates]# 

-bash: catxx: command not found 并没有在文件内容中出现。这是因为这些
信息属于stderr,而tee只能从stdin中读取。


使用stdin作为命令参数。只需要将-作为命令的文件名参数即可

我们可以使用stdin作为命令参数。只需要将-作为命令的文件名参数即可

$ cmd1 | cmd2 | cmd -

例如:

[root@entel1 Templates]# echo xiaogongjiang |tee -
xiaogongjiang
xiaogongjiang

也可以将 /dev/stdin作为输出文件名来代替stdin。
类似地,使用
/dev/stderr代表标准错误,
/dev/stdout代表标准输出。
这些特殊的设备文件分别对应stdin、 stderr和stdout。


工作原理

就输出重定向而言, >和>>并不相同。尽管两者可以将文本重定向到文件,但是>会先清空文件,然后再写入内容,而>>会将内容追加到现有文件的尾部。

当使用重定向操作符时,输出内容不会在终端打印,而是被导向文件。重定向操作符默认使用标准输出。

如果想使用特定的文件描述符,你必须将描述符编号置于操作符之前。

>等同于1>;对于>>来说,情况也类似(即>>等同于1>>)。

处理错误时,来自stderr的输出被丢弃到文件/dev/null中。 /dev/null是一个特殊的设备文件,它接收到的任何数据都会被丢弃。

null设备通常也被称为黑洞,因为凡是到这儿的数据都将一去不返。


补充内容

从stdin读取输入的命令能以多种方式接收数据。也可以用cat和管道来指定我们自己的文件描述符,例如:

$ cmd1 | cmd

举例

[root@entel1 Templates]# cat a.txt 
xiaogongjiang
[root@entel1 Templates]# cat a.txt | wc -lc #统计行数 字数
      1      14

将文件重定向到命令

借助重定向,我们可以像使用stdin那样从文件中读取数据:

$ cmd < file

栗子

[root@entel1 Templates]# cat a.txt 
xiaogongjiang
[root@entel1 Templates]# wc -lc < a.txt 
 1 14

将脚本内部的文本块进行重定向

有时候,我们需要对文本块(多行文本)进行重定向,就像对标准输入做的那样。

考虑一个特殊情况:源文本就位于shell脚本中。

一个实用的例子是向log文件中写入头部数据,可以按照下面的方法完成:

#!/bin/bash
cat<<EOF>log.txt
LOG FILE HEADER
This is a test log file
Function: System statistics
EOF

EOF: 文件结束符 end of file

在cat <<EOF>log.txt与下一个EOF行之间的所有文本行都会被当做stdin数据。

log.txt文件的内容打印如下:

$ cat log.txt
LOG FILE HEADER
This is a test log file
Function: System statistics

http://blog.csdn.net/yangshangwei/article/details/52388349?locationNum=1&fps=1#t9

[root@entel1 Templates]# cat << EOF>a.txt
> pwd
> dskfsdkfs
> 
> dsfs 
> EOF
[root@entel1 Templates]# cat a.txt 
pwd
dskfsdkfs

dsfs 
[root@entel1 Templates]# 

自定义文件描述符

省略…..好像没啥用到的场景…..

上一篇:[AWS DA] API Gateway: Mapping Templates


下一篇:unity 引用 移动mm 支付sdk