Shell重定向

Shell重定向

输入和输出

在Linux中一切皆文件,即使是硬件,在Linux系统中同样的表示为文件。有三个标准的POSIX字符,被成为文件描述符。每个Linux命令都会使用到0、1、2三个文件描述符与用户或其他系统程序进行交互。

  • 0——标准输入——键盘:从文件(默认是键盘)读取输入

  • 1——标准输出——屏幕:发送数据到文件(默认为屏幕)

  • 2——标准错误——屏幕:发送所有错误信息到一个文件(默认为屏幕)

标准输入

在Shell运行任何命令之前,都会尝试打开文件进行读取。如果打开文件失败,Shell将以一个错误的退出并不运行命令。如果打开成功,Shell使用打开的文件的文件描述符作为命令的标准输入文件描述符。

标准输入具有如下特点:

  1. 它是默认的输入方法,它被所有命令使用来读取输入

  2. 它用数字0表示

  3. 也被称为stdin

  4. 默认的标准输入设备是键盘

操作符"<"是输入重定向操作符,语法:

command < input_file

如:

[root@rs1 io]# cat < 1.txt 
狗肉汤就是用狗肉炖的汤

标准输出

标准输出有如下特点:

  1. 它被命令用来写入或显示命令自身的输出

  2. 它用数字1表示

  3. 它也被成为stdout

  4. 默认的标准输出设备是屏幕

操作符"<"是输出重定向操作符,语法:

command > out_file

Shell首先尝试打开用于写入的文件out_file,如果成功,就将命令的标准输出发送到打开的文件中;如果打开文件失败,整个命令失败。

command > out_file和command 1>out_file的意义相同

如:

[root@rs1 io]# ls > 2.txt
[root@rs1 io]# cat 2.txt 
1.txt
2.txt
如果输出文件不存在,系统会自动创建。如果存在,使用">"会被重写

标准错误

标准错误具有如下特点:

  1. 它是默认的错误输出方法,被用于写入所有系统错误信息

  2. 它用数字2表示

  3. 它也被成为stderr

  4. 默认的标准错误输出设备是屏幕

操作符"2>"是标准错误重定向操作符,语法:

command 2> error_file

如:

[root@rs1 io]# cajig 2> error_txt
[root@rs1 io]# cat error_txt 
-bash: cajig: command not found
由于cajig命令不存在,系统会提示错误信息,2>将标准错误重定向到了error_txt文件中

重定向

在LInux中,总有三个默认的设备文件是打开的,即stdin、stdout、stderr。这三个文件和其他任何打开的文件都可以被重定向。所谓重定向就是指从文件、命令、程序、脚本,甚至脚本中的代码块获得输出并把它作为输入写入另一个文件、命令、程序或脚本中。

每个打开的文件都对应一个文件描述符(非负整数)。stdin(0)、stdout(1)、stderr(2),所以当打开其他文件时,文件描述符号从3开始。

文件重定向

文件重定向时更改一个文件描述符以指向一个文件。

如:(发送标准错误到一个文件)

[root@rs1 io]# cat stderr_1.sh 
#!/bin/bash#如果参数小于1,则提示信息,并退出脚本
if test $# -lt 1
then
    echo "Usage: $0 DIRECTORY..."
    exit 1
fi#使用for循环遍历所有参数
for i in $@
do
    #找到指定目录中以.tmp为后缀的文件,并删除
    find $i -name "*.tmp" -exec rm -rf {} \;
#将for循环产生的错误写入到文件error.log中
done 2> error.log

注意:在脚本文件中,重定向符并没有在rm命令之后,因为,重定向应用于循环内生成的所有标准错误的输出,Bash在循环开始前就已经打开了error.log文件(如果不存在,先创建),并将标准错误指向它,然后当循环结束时,关闭它。运行在循环内部的所有命令都从Bash继承打开的文件描述符。

">"是标准输出重定向操作符,但是它会覆盖之前定向到文件里的所有内容,">>"则表示将标准输出追加进指定的文件中(不对之前文件内存在的数据进行操作)。

从文件输入

我们有时需要使用一个代码块使用重定向读取文件的内容。

如:

[root@rs1 io]# cat std_1.sh 
#!/bin/bash#如果参数个数不为1,则执行if语句
if test $# -ne 1
then
    #打印脚本使用方法
    echo "Usage: $0 FILEPATH..."
    #退出
    exit 1
fi#定义一个变量file,并将脚本执行的第一个参数赋值给file
file=$1#定义一个代码块
{
    #读取一行内容,并将读取内容存放到变量line1中
    read line1
    #读取一行内容,并将读取的内容存放到变量line2中
    read line2
#将这个代码块的标准输入指向变量file所对应的文件
} < $file#打印变量file的值和一些信息
echo "First line in $file is : "
#打印变量line1的值
echo "$line1"#打印变量file的值和一些信息
echo "Second line in $file is : "
#打印变量line2的值
echo "$line2"
#退出脚本,并且退出状态码为0
exit 0

执行脚本:

[root@rs1 io]# bash std_1.sh    //没有添加参数执行结果 
Usage: std_1.sh FILEPATH...
​
[root@rs1 io]# bash std_1.sh ./1.txt    //添加参数为当前目录下的1.txt文件
First line in ./1.txt is : 
狗肉汤就是用狗肉炖的汤
Second line in ./1.txt is : 
ddif
​
[root@rs1 io]# cat 1.txt //1.txt中的内容
狗肉汤就是用狗肉炖的汤
ddif
fdisk
zone
tty

该脚本使用重定向从指定的文件中读取文件头两行内容。脚本中大括号"{}"之间的代码称之为一个代码块,然后使用"<$"将这个代码块的标准输入指向"$file"中

有时候我们需要逐行读取内容,并对没一行数据进行特殊处理。这时,最好的解决方法就是将循环语句和重定向结合起来使用,读取并处理文件内容。

如:(while循环和重定向组合)

[root@rs1 io]# cat std_2.sh 
#!/bin/bash#如果指定给脚本的参数个数不为1,则执行if语句
if test $# -ne 1
then
    echo "Usage: `basename $0` FILE_PATH..."
    exit 1
fi#定义变量filename和count,并将脚本的第一个参数赋值给filename变量
filename=$1
count=0#使用while循环逐行读取内容,并将内容存入变量LINE
while read LINE
do
    #count自增,记录读取到多少行
    let count++
    #输出当前行的信息
    echo "the $count Line contxt is:"
    echo "$LINE"
done < $filename    #这个重定向将整个while循环的标准输入指向了文件$filenameecho "Total $count has been read over"
exit 0

执行脚本:

[root@rs1 io]# bash std_2.sh 
Usage: std_2.sh FILE_PATH...
[root@rs1 io]# bash std_2.sh 1.txt 
the 1 Line contxt is:
狗肉汤就是用狗肉炖的汤
the 2 Line contxt is:
ddif
the 3 Line contxt is:
fdisk
the 4 Line contxt is:
zone
the 5 Line contxt is:
tty
Total 5 has been read over

如果使用if循环,但是这种方法只会读取文件第一行内容

[root@rs1 io]# cat std_3.sh 
#!/bin/bashif test $# -ne 1
then
    echo "Usage: `basename $0` FILE_PATH..."
    exit 1
fifilename=$1
count=0if true
then
    read LINE
    let count++
    echo "The $count contxt is:"
    echo "$LINE"
fi < $filenameecho "Total $count has been read over"
exit 0

执行脚本:

[root@rs1 io]# bash std_3.sh 
Usage: std_3.sh FILE_PATH...
[root@rs1 io]# bash std_3.sh 1.txt 
The 1 contxt is:
狗肉汤就是用狗肉炖的汤
Total 1 has been read over

从文本或字符串输入

Bash还有一种重定向的类型是here-documents,这种重定向的操作符是"<<MARKER"。这种操作符指示Bash从标准输入读取输入的内容,直到读取到只包含MARKER的行为止。其语法:

command <<MARKER
    HERE DOCUMENTS
MARKER

MARKER可以是任何单词作为标志。

如:

[root@rs1 io]# tr a-z A-Z <<EOF
one two three
fou five six
EOF
​
ONE TWO THREE
FOU FIVE SIX

重定向操作符"<<"和边界标示符"EOF"之间不需要空格分割。在"<<"和EOF之间添加"-",将会忽略行首的制表符。这使Shell脚本中缩进的here-documents的值不会改变。

[root@rs1 io]# cat heredoc_1.sh 
#!/bin/bash
​
#将here-documents中的内容转换为大写
tr a-z A-Z <<EOF
    one two three
    Four Five Six
EOF
​
#将here-documents中的内容转换为大写,忽略行首制表符
tr a-z A-Z <<-EOF
    seven eight nine
    ten
EOF

执行脚本:

[root@rs1 io]# bash heredoc_1.sh 
    ONE TWO THREE
    FOUR FIVE SIX
SEVEN EIGHT NINE
TEN

默认情况下,Bash替换会再here-documents的内容上执行,即here-documents内部的变量和命令回被执行。如:

[root@rs1 io]# cat <<EOF
> Working dir is $PWD
> EOF
Working dir is /mnt/io

here-string是here-documents的一个变种。它由操作符"<<<"和作为标准输入的字符串构成,here-string是一个用于输入重定向的普通字符串。语法:

command <<<WORD

单个单词不需要引号引用,中间如果有空格的字符串,则需要引号引用起来。

如:

[root@rs1 io]# tr a-z A-Z <<<one
ONE
[root@rs1 io]# tr a-z A-Z <<<"one two"
ONE TWO
[root@rs1 io]# tr a-z A-Z <<<one two
tr: extra operand ‘two’
Try 'tr --help' for more information.

空文件创建

创建空文件的语法:

 > filename

操作符">"重定向输出到一个文件。如果没有命令指定并且文件不存在的话,Bash将会创建一个空文件。

如:

[root@rs1 io]# cat empty_file.sh 
#!/bin/bash#定义变量tarcmd
tarcmd=/bin/tar
​
#存储路径
store=/store
​
#需要备份的目录名
backdirs="/var/www/html /mnt"#日志文件的名字
errlog=/tmp/tarbackup.error
​
#当前日期
today=`date +%Y%m%d`#删除就得日志文件并创建新的空文件
>$errlog#使用tar命令将需要备份的目录备份到指定目录里面,并将错误输出重定向到errlog所指向的文件
$tarcmd -cvf $store/$today.tar $backdirs 2>$errlog

执行脚本:

[root@rs1 io]# bash empty_file.sh 
[root@rs1 io]# ls /store/
20180710.tar
[root@rs1 io]# ls error.log 
error.log

>$errlog在每次运行tar之前,先清空之前的日志文件内容

丢掉不需要的输出

写入到/dev/null的所有数据都被系统丢弃。所以可以将任何不需要的程序或者命令的输出发送到/dev/null中。

如:

当我们在/etc/passwd中搜索用户root时,只是想得到找到或者没有找到的信息。

[root@rs1 io]# grep "^root" /etc/passwd && echo "user \"root\" was found" || echo "user \"root\" does not exits"
root:x:0:0:root:/root:/bin/bash
user "root" was found
//这中方式,直接显示了root的相关信息,我们不需要回显root信息,可以将这段显示扔进/dev/null中
​
[root@rs1 io]# grep "^root" /etc/passwd > /dev/null && echo "user \"root\" was found" || echo "user \"root\" does not exits"
user "root" was found

标准错误重定向

command 2> error.log

标准输出重定向

command 1> output_file
command > output_file

标准输出、错误同时重定向

command &> file
commadn >& file
command > file 2>&1
command 2>&1 > file

如:

[root@rs1 io]# bash -x empty_file.sh &> debug.log
[root@rs1 io]# cat debug.log 
+ tarcmd=/bin/tar
+ store=/store
+ backdirs='/var/www/html /mnt'
+ errlog=/tmp/tarbackup.error
++ date +%Y%m%d
+ today=20180710
+ /bin/tar -cf /store/20180710.tar /var/www/html /mnt

当我们在编译某个安装包时,可以使用如下命令:

(./configure && make && make install) > /tmp/make.log 2&>1

或者:

(./configure && make && make install) 2>&1 /tmp/make.log

追加重定向输出

符号">>"用于追加重定向输出。语法:

command >> filename

在单命令行进行标准输入输出重定向

可以在一条命令行中完成标准输入输出重定向。语法:

command < input_file >output_file

或:

< input_file command > outout_file

如:(我们需要将一个文件中所有内容转化为大写,并重写到一个新的文件中)

[root@rs1 io]# cat 1.txt 
狗肉汤就是用狗肉炖的汤
ddif
fdisk
zone
tty
​
[root@rs1 io]# tr a-z A-Z < 1.txt > new_file
[root@rs1 io]# cat new_file 
狗肉汤就是用狗肉炖的汤
DDIF
FDISK
ZONE
TTY

这里有一个例子,再一个命令中进行标准输入输出重定向,用于解包一个rpm归档文件:

[root@rs1 derpm]# cat derpm.sh 
#!/bin/bash#如果指定给脚本的参数个数不为1,则执行if语句
if test $# -ne 1
then
    echo "Usage: `basename $0` target_file"
    exit 1
fi#定义变量TEMPFILE,并指定一个唯一的临时文件名作为变量的值
TEMPFILE=/tmp/$$.cpio
​
#使用rpm2cpio命令,将脚本的第一个参数所代表的rpm归档文件转换为变量TEMPFILE所代表的cpio归档文件
rpm2cpio < $1 > $TEMPFILE
#使用cpio命令,将TEMPFILE所代表的cpio归档文件进行解包
cpio --make-directories -F $TEMPFILE -i
#删除cpio归档文件
rm -f $TEMPFILE#退出脚本,且退出状态码为0
exit 0

执行脚本:

[root@rs1 derpm]# bash derpm.sh 
Usage: derpm.sh target_file
[root@rs1 derpm]# bash derpm.sh httpd-2.4.6-45.el7.x86_64.rpm 
7703 blocks
[root@rs1 derpm]# ls
derpm.sh  etc  httpd-2.4.6-45.el7.x86_64.rpm  run  usr  var

使用rpm2cpio命令先将指定的rpm归档文件转换为cpio归档文件,再使用cpio命令进行解包。

文件描述符

Shell有时会引用使用文件描述符(fd)的文件。我们一般使用文件描述符的范围是0~9。重定向大于9的文件描述符要谨慎,因为它们可能于Shell内部使用的文件描述符冲突。

使用exec命令

Bash的内部命令exec的功能之一就是允许操作文件描述符。如果再exec后没有指定命令,则exec命令之后的重定向将更改当前Shell的文件描述符号。

如:

[root@rs1 exec]# cat exec_1.sh 
#!/bin/bash#如果没有指定参数,则执行if语句
if [[ $# -eq 0 ]]
then
    echo "Usage: `basename $0` filename..."
    exit 1
fi#将指定给脚本的第一个参数赋值给变量file
file=$1#逐行读取文件内容,并将读取的数据存入变量line中
while read -r line
do
    echo $line
#等待用户键入任意键
    read -p "Press any key to continue." -n 1
done < $file    #将while循环的标准输入指向变量file所代表的文件

执行脚本:

[root@rs1 exec]# cat 1.txt 
line 1 狗肉汤
line 2 爱国者
line 3 coco
line 4 FSX
​
[root@rs1 exec]# bash exec_1.sh 1.txt 
line 1 狗肉汤
ine 2 爱国者
ine 3 coco
ine 4 FSX
[root@rs1 exec]# bash exec_1.sh 
Usage: exec_1.sh filename...
​

执行过程中"read -p "Press any key to continue." -n 1"这句命令并没有被执行。因为我们将1.txt作为输入重定向到while循环中,while循环中所有的命令都继承了这个文件描述符,因此read将重定向后的标准输入,而不再将键盘作为标准输入。

所以,因该做如下修改:

[root@rs1 exec]# cat exec_2.sh 
#!/bin/bash#如果没有指定参数,则执行if语句
if [[ $# -eq 0 ]]
then
    echo "Usage: `basename $0` filename..."
    exit 1
fi#将脚本的第一个参数作为输入文件,并指定其文件描述符为3
exec 3< $1#逐行读取文件内容,并将读取的数据存入变量line中
while read -u 3 line
do
    echo $line
#等待用户键入任意键
    read -p "Press any key to continue." -n 1
done
​
exec 3<&-

执行脚本:

[root@rs1 exec]# bash exec_2.sh 
Usage: exec_2.sh filename...
[root@rs1 exec]# bash exec_2.sh 1.txt 
line 1 狗肉汤
Press any key to continue.
line 2 爱国者
Press any key to continue.
line 3 coco
Press any key to continue.
line 4 FSX
Press any key to continue.
​
Press any key to continue.

再脚本里面使用了"read -u 3 line",其含义:-u选项可以指定从特定的文件描述符中读取数据,3标示读取文件描述符为3的文件。这对于逐行地读取文件内容或者依次读取一个单词很有用。

指定用于输入的文件描述符

Shell允许给一个输入文件或输出文件指定一个文件描述符。这样可以提高文件读写的性能。这类文件描述符被成为用户自定义文件描述符。语法:

exec [n]<file

[n]就是指定的文件描述符,如果不指定n,这使用stdin(0)。上述的输入重定向再文件描述符n上打开一个用于读取的文件file。

如:

[root@rs1 exec]# cat exec_3.sh 
#!/bin/bash#如果没有指定参数,则执行if语句
if [[ $# -eq 0 ]]
then
    echo "Usage: `basename $0` filename..."
    exit 1
fi#将脚本的第一个参数作为输入文件,并指定一个文件描述符3
exec 3< $1#将标准输入作为文件描述符3的副本
cat <& 3
#关闭文件描述符号3
exec 3<&-
​

执行脚本:

[root@rs1 exec]# bash -x exec_3.sh 1.txt 
+ [[ 1 -eq 0 ]]
+ exec
+ cat
line 1 狗肉汤
line 2 爱国者
line 3 coco
line 4 FSX
​
+ exec
[root@rs1 exec]# bash -x exec_3.sh 
+ [[ 0 -eq 0 ]]
++ basename exec_3.sh
+ echo 'Usage: exec_3.sh filename...'
Usage: exec_3.sh filename...
+ exit 1

再通过一个例子,进一步了解exec命令用于输入的文件描述符:

[root@rs1 exec]# cat exec_4.sh 
#!/bin/bash#将标准输入赋值到文件描述符6,保存标准输入
exec 6<&0
​
#将文件/etc/hosts重定向到标准输入
exec < /etc/hosts
​
#读取文件/etc/hosts文件第一行,并将数据保存到变量line1中
read line1
#读取文件/etc/hosts文件第二行,并将数据保存到变量line2中
read line2
​
cat <<-EOF
    Following lines read form file
    ------------------------------
    first line contxt:
    `echo $line1`
    second line contxt:
    `echo $line2`
EOF
​
#从文件描述符6中恢复标准输入,并关闭文件描述符6
exec <&6 6<&-
​
echo -n "Enter data :"
#现在,read命令从标准输入中读取数据,并赋值给Line1
read Line1
cat <<-EOF
    Innput read form stdin
    ----------------------
    `echo "stdin input :" $Line1`
EOF
​

执行脚本:

[root@rs1 exec]# bash exec_4.sh 
Following lines read form file
------------------------------
first line contxt:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
second line contxt:
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
Enter data :fsx123      //此处需要使用键盘键入数据,因为此是键盘已经成为了命令继承的输入
Innput read form stdin
----------------------
stdin input : fsx123

指定用于输出的文件描述符

给一个输出文件指定一个文件描述符的语法:

exec [n]>file

其中,[n]就是文件描述符,如果不指定,则默认为1,即stdout。

上述的语法,再输出重定向时,会在文件描述符n上打开一个file,用于保存输出。如果文件不存在,系统会创建,如果已经存在,则回被清0。

如:

[root@rs1 exec]# exec 4>file    //将标准输出重定向到文件描述符为4的发ile文件中
[root@rs1 exec]# date >& 4      //执行结果复制到文件描述符4
[root@rs1 exec]# cat file 
Wed Jul 11 00:40:16 CST 2018

这里的">&"不时标准输入输出时用于重定向的操作符,它提供了复制输出文件描述符的能力。语法:

[n] >& n

如果n没有指定,则默认使用stdout。如果数字n指定的文件描述符没有被打开用于输出,会发生重定向错误。

[root@rs1 exec]# date >& 3
date: write error: Bad file descriptor

这里有一个脚本来进一步理解输出文件描述符:

[root@rs1 exec]# cat exec_5.sh 
#!/bin/bash#定义变量LOGFILE并赋值
LOGFILE=./logfile.txt
​
#复制文件描述符6到标准输出
exec 6>&1
#重定向标准输出到变量LOGFILE所代表的文件中
exec > $LOGFILE#之后在关闭文件描述符6之前,所有的输出都被重写到LOGFILE中echo -n "Logfile:"
echo
echo "date command"
date
echo "------------------"echo "Output of \"uname -r \" command"
uname -r
echo "------------------"echo "Outpuf of \"df\" command"
df -h#恢复标准输出并关闭文件描述符6
exec 1>&6 6>&-
​
echo "==stdout now restored to default=="
ifconfig eth0
​
#退出脚本,并且退出码为0
exit 0

执行脚本:

[root@rs1 exec]# bash exec_5.sh 
==stdout now restored to default==
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.25.254.15  netmask 255.255.0.0  broadcast 172.25.255.255
        inet6 fe80::5054:ff:feb1:f80  prefixlen 64  scopeid 0x20<link>
        ether 52:54:00:b1:0f:80  txqueuelen 1000  (Ethernet)
        RX packets 111804  bytes 11287583 (10.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 65803  bytes 10698124 (10.2 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
​
[root@rs1 exec]# cat logfile.txt 
Logfile:
date command
Wed Jul 11 00:54:51 CST 2018
------------------
Output of "uname -r " command
3.10.0-123.el7.x86_64
------------------
Outpuf of "df" command
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root  4.9G  919M  4.0G  19% /
devtmpfs               239M  4.0K  239M   1% /dev
tmpfs                  246M     0  246M   0% /dev/shm
tmpfs                  246M  8.4M  237M   4% /run
tmpfs                  246M     0  246M   0% /sys/fs/cgroup
/dev/vda1              497M   97M  401M  20% /boot

一个实战脚本,深化执行命令并将命令的结果发送到指定的文件描述符:

[root@rs1 test]# cat exec.sh 
#!/bin/bash#定义变量NOW,值为当前日期。格式:yyyymmdd
NOW=`date +%Y%m%d`
#定义变量OUTPUT
OUTPUT=./output.txt
​
exec 3>&1
#在文件描述符3上打开变量OUTPUT文件,用于写入
exec >$OUTPUT
echo "The Information $NOW collect as:"
cat <<EOF
    ---------------------------------
    System Info run @ $(date) for $(hostname)
    ---------------------------------
EOF
​
cat <<-EOF
    ********************************
    ******Installed Hard Disk*******
    ********************************
EOF
#显示系统中已经安装的磁盘
fdisk -l |egrep "^Disk /dev"
echo ----------------------------------------------
echocat <<-EOF
    ********************************
    **File System Disk Space Usage**
    ********************************
EOF
#显示文件系统磁盘使用情况
df -H
echo ----------------------------------------------
echocat <<-EOF
    ********************************
    ********Cpu Information*********
    ********************************
EOF
#显示cpu类型
grep 'model name' /proc/cpuinfo | uniq | awk -F: '{ print $2 }'
echo ----------------------------------------------
echo
​
​
cat <<-EOF
    ********************************
    *****Operating System Info******
    ********************************
EOF
#显示系统信息
uname -a 
echo ----------------------------------------------
echorelease=/usr/bin/lsb_release
#如果文件/usr/bin/lsb_release存在并且可执行,则打印系统发行版本所有信息,否则提示文件不存在
[ -x $release ] && $release -a || echo " file $release does not exist!"cat <<-EOF
    ********************************
    *Amount Of Free And Used Memory*
    ********************************
EOF
#显示剩余内存和使用的内存
free -m
echo ----------------------------------------------
echocat <<-EOF
    ********************************
    **Top 10 Memory Eating Process**
    ********************************
EOF
#显示最消耗内存的10个进程
ps -auxf | sort -nr -k 4 | head -10
echo ----------------------------------------------
echocat <<-EOF
    ********************************
    ***Top 10 CPU Eating Process****
    ********************************
EOF
#显示最消耗cpu的10个进程
ps -auxf | sort -nr -k 3 |head -10
echo ----------------------------------------------
echocat <<-EOF
    *************************************
    **Network Device Information [eth0]**
    *************************************
EOF
#显示第一块网卡的信息
netstat -i | grep -q eth0 && ifconfig eth0 || echo "eth0 is not installed"
echo ----------------------------------------------
echocat <<-EOF
    *************************************
    *******Wireless Devicce [wlan0]******
    *************************************
EOF
#显示无限网咖信息,如果不存在提示
netstat -i | grep -q wlan0 && ifconfig wlan0 || echo "wlan0 is not installed"
echo ----------------------------------------------
echocat <<-EOF
    *************************************
    *****All Network Interfaces Stats****
    *************************************
EOF
#显示所有网卡的状态
netstat -i
echo ----------------------------------------------
echo
​
exec 1>&3 3>&-
​
echo "System info wrote to $OUTPUT file"

执行脚本:

[root@rs1 test]# bash exec.sh 
System info wrote to ./output.txt file

输出内容几乎都被重定向到文件描述符打开的output.txt文件中,内容如下:

[root@rs1 test]# cat output.txt 
The Information 20180711 collect as:
    ---------------------------------
    System Info run @ Wed Jul 11 01:47:10 CST 2018 for rs1
    ---------------------------------
********************************
******Installed Hard Disk*******
********************************
Disk /dev/vda: 6442 MB, 6442450944 bytes, 12582912 sectors
Disk /dev/mapper/rhel-root: 5268 MB, 5268045824 bytes, 10289152 sectors
Disk /dev/mapper/rhel-swap: 645 MB, 645922816 bytes, 1261568 sectors
----------------------------------------------
​
********************************
**File System Disk Space Usage**
********************************
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root  5.3G  964M  4.3G  19% /
devtmpfs               251M  4.1k  251M   1% /dev
tmpfs                  257M     0  257M   0% /dev/shm
tmpfs                  257M  8.8M  249M   4% /run
tmpfs                  257M     0  257M   0% /sys/fs/cgroup
/dev/vda1              521M  101M  420M  20% /boot
----------------------------------------------
​
********************************
********Cpu Information*********
********************************
 Intel Xeon E312xx (Sandy Bridge)
----------------------------------------------
​
********************************
*****Operating System Info******
********************************
Linux rs1 3.10.0-123.el7.x86_64 #1 SMP Mon May 5 11:16:57 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
----------------------------------------------
​
 file /usr/bin/lsb_release does not exist!
********************************
*Amount Of Free And Used Memory*
********************************
             total       used       free     shared    buffers     cached
Mem:           490        200        289          8          0         85
-/+ buffers/cache:        114        375
Swap:          615          0        615
----------------------------------------------
​
********************************
**Top 10 Memory Eating Process**
********************************
root       568  0.0  3.2 549976 16068 ?        Ssl  Jul05   0:50 /usr/bin/python -Es /usr/sbin/tuned -l -P
root       561  0.0  1.7 377920  8684 ?        Ssl  Jul05   0:01 /usr/sbin/NetworkManager --no-daemon
polkitd    615  0.0  1.4 513848  7496 ?        Ssl  Jul05   0:00 /usr/lib/polkit-1/polkitd --no-debug
root      7762  0.0  0.9 131524  4840 ?        Ss   Jul10   0:01  \_ sshd: root@pts/0
root      5608  0.0  0.9 131524  4836 ?        Ss   Jul08   0:00  \_ sshd: root@pts/1
root       828  0.0  0.7  82956  3608 ?        Ss   Jul05   0:00 /usr/sbin/sshd -D
root         1  0.0  0.7  47480  3728 ?        Ss   Jul05   0:00 /usr/lib/systemd/systemd --switched-root --system --deserialize 23
postfix   8068  0.0  0.7  91804  3904 ?        S    01:22   0:00  \_ pickup -l -t unix -u
postfix   1806  0.0  0.7  91872  3924 ?        S    Jul05   0:00  \_ qmgr -l -t unix -u
root      7798  0.0  0.4 115452  2144 pts/0    S+   Jul10   0:00          \_ bash
----------------------------------------------
​
********************************
***Top 10 CPU Eating Process****
********************************
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root       910  0.0  0.1 113280   744 ?        Ss   Jul05   0:00 /usr/bin/rhsmcertd
root         9  0.0  0.0      0     0 ?        S    Jul05   0:00  \_ [rcuob/0]
root      8375  0.0  0.1 107900   664 pts/1    S+   01:47   0:00  |           \_ head -10
root      8374  0.0  0.0 113120   204 pts/1    R+   01:47   0:00  |           \_ bash exec.sh
root      8373  0.0  0.2 123512  1436 pts/1    R+   01:47   0:00  |           \_ ps -auxf
root      8350  0.0  0.2 113120  1464 pts/1    S+   01:47   0:00  |       \_ bash exec.sh
root       828  0.0  0.7  82956  3608 ?        Ss   Jul05   0:00 /usr/sbin/sshd -D
root      8228  0.0  0.0      0     0 ?        S    01:43   0:00  \_ [kworker/0:2]
root        81  0.0  0.0      0     0 ?        S    Jul05   0:00  \_ [kauditd]
----------------------------------------------
​
*************************************
**Network Device Information [eth0]**
*************************************
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.25.254.15  netmask 255.255.0.0  broadcast 172.25.255.255
        inet6 fe80::5054:ff:feb1:f80  prefixlen 64  scopeid 0x20<link>
        ether 52:54:00:b1:0f:80  txqueuelen 1000  (Ethernet)
        RX packets 119843  bytes 12033485 (11.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 70920  bytes 11532462 (10.9 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
​
----------------------------------------------
​
*************************************
*******Wireless Devicce [wlan0]******
*************************************
wlan0 is not installed
----------------------------------------------
​
*************************************
*****All Network Interfaces Stats****
*************************************
Kernel Interface table
Iface      MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0      1500   119843      0      0 0         70920      0      0      0 BMRU
lo       65536        0      0      0 0             0      0      0      0 LRU
----------------------------------------------

关闭文件描述符

在之前已经反复使用了这个操作。语法:

[n] <&-
//关闭标准输入

或者

[n] >&-
//关闭文件描述符,如果n=2就是关闭标准错误

主动关闭手动打开的文件描述符是一个好习惯。

打开用于读写的文件描述符

Bash支持使用如下语法在文件描述符上打开一个可读写的文件:

exec [n]<>file

其中n是文件描述符,如果不指定则默认标准输入。如果文件file不存在,则先创建。符号"<>"称为菱形操作符,标示打开一个可读写的文件。

这个语法对更新文件很有用。

如:

#将字符串"one two"写入到文件file1中
[root@rs1 test]# echo "one two" > file1
​
#在文件描述符4上打开文件file1
[root@rs1 test]# exec 4<> file1 
​
#从文件描述符4上读取前5个字节
[root@rs1 test]# read -n 5 var <& 4
[root@rs1 test]# echo $var
one t

可以看到,var保存了最开始写入内容的前5个字节,然后想文件描述符里写入数据

[root@rs1 test]# echo -n + >& 4
[root@rs1 test]# exec 4>&-
[root@rs1 test]# cat file1 
one t+o

我们可以看到,结果是"one t+o",而不是"one two+"。值和是因为,我们先用read读取了前5个字节,而操作符"<>"会使后续的读写操作跟随先前读写操作的位置(就是说文件指针没有在最后面,二是停留在该生命周期内,最后一次读写到的位置),所以+会被写在地6个字符位置。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值