Shell教程—Shell输入/输出重定向

Linux Shell 重定向分为两种,一种输入重定向,一种是输出重定向;从字面上理解,输入输出重定向就是改变输入与输出的方向的意思。

一般情况下,我们以CPU 为参考,CPU读入数据就是输入,CPU写出数据就是输出。在Linux中从键盘输入数据被称为标准输入,向屏幕/显示器上显示数据被称为标准输出。

一、硬件设备和文件描述符

1、文件描述符定义

计算机的硬件设备有很多,常见的输入设备有键盘、鼠标、麦克风、手写板等,输出设备有显示器、投影仪、打印机等。不过,在 Linux 中,标准输入设备指的是键盘,标准输出设备指的是显示器。

Linux 中一切皆文件,包括标准输入设备(键盘)和标准输出设备(显示器)在内的所有计算机硬件都是文件。为了表示和区分已经打开的文件,Linux 会给每个文件分配一个 ID,这个 ID 就是一个整数,被称为文件描述符(File Descriptor)。

文件描述符 文件名 类型
0 stdin 标准输入文件
1 stdout 标准输出文件
2 stderr 标准错误输出文件
3以上 filiename 其他文件

Linux 程序在执行任何形式的 I/O 操作时,都是在读取或者写入一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数,它的背后可能是一个硬盘上的普通文件、FIFO、管道、终端、键盘、显示器,甚至是一个网络连接。

stdin、stdout、stderr 默认都是打开的,在重定向的过程中,0、1、2 这三个文件描述符可以直接使用。

2、查看一个进程打开了哪些文件

语法: ll /proc/[进程ID]/fd

/proc/[进程ID]/fd这个目录专门用于存放文件描述符。0、1、2也就是宏STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。

二、Linux Shell输出重定向

通过上面的学习我们了解到Linux标准输出是显示器,输出重定向就是将指令命令的结果不在输出到显示器上,而是输出到其它地方,一般是指定文件中。这样做的最大好处就是把命令的结果保存起来,当我们需要的时候可以随时查询。Bash 支持的输出重定向符号如下表所示:

类型符号作用
标准输出重定向 command >file 以覆盖的方式,把命令command的正确执行结果输出到文件file中
command >>file> 以追加的方式,把命令command的正确执行结果输出到文件file中
标准错误输出重定向 command 2>file 以覆盖的方式,把命令command的错误执行结果输出到文件file中
command 2>>file 以追加的方式,把命令command的错误执行结果输出到文件file中
正确输出和错误信息同时保存 command >file 2>&1 以覆盖的方式,把正确输出和错误信息同时保存到同一个文件(file)中
command >>file 2>&1 以追加的方式,把正确输出和错误信息同时保存到同一个文件(file)中
command >file1 2>file2 以覆盖的方式,把正确的输出结果输出到 file1 文件中,把错误信息输出到 file2 文件中。
command >>file1 2>>file2 以追加的方式,把正确的输出结果输出到 file1 文件中,把错误信息输出到 file2 文件中。
command >file 2>file 【不推荐】这两种写法会导致 file 被打开两次,引起资源竞争,所以 stdout 和 stderr 会互相覆盖
command >>file 2>>file>
注意

在文件重定向中我们使用了两个关键点的符号:>>>,其中前者表示覆盖原来的文本,后者表示在原来的文本后面追加。其实文重定向的标准写法是:fd>file或者fd>>file,其中fd表示文件描述符,不写默认为1,也就表示标准输出。

当文件描述符为 1 时,一般都省略不写,如上表所示;当然,如果你愿意,也可以将command >file写作command 1>file,但这样做是多此一举。当文件描述符为大于 1 的值时,比如 2,就必须写上。

需要重点说明的是,fd>之间不能有空格,否则 Shell 会解析失败;>file之间的空格可有可无。为了保持一致,我习惯在>两边都不加空格。

案例

(1)使用echo命令将Shell脚本的运行结果输出到指定的文件中

写一个脚本,检测当前系统对CPU的使用率,把脚本加入到crontab 定时任务中,每分钟执行一次,并把执行结果输出到/home/cpu_monitor.txt文本中

#!/bin/bash
#
#脚本功能描述:依据/proc/stat文件获取并计算CPU使用率
#
#CPU时间计算公式:CPU_TIME=user+system+nice+idle+iowait+irq+softirq
#CPU使用率计算公式:cpu_usage=(idle2-idle1)/(cpu2-cpu1)*100
#默认时间间隔
TIME_INTERVAL=2
time=$(date "+%Y-%m-%d %H:%M:%S")
LAST_CPU_INFO=$(cat /proc/stat | grep -w cpu | awk '{print $2,$3,$4,$5,$6,$7,$8}')
LAST_SYS_IDLE=$(echo $LAST_CPU_INFO | awk '{print $4}')
LAST_TOTAL_CPU_T=$(echo $LAST_CPU_INFO | awk '{print $1+$2+$3+$4+$5+$6+$7}')
sleep ${TIME_INTERVAL}
NEXT_CPU_INFO=$(cat /proc/stat | grep -w cpu | awk '{print $2,$3,$4,$5,$6,$7,$8}')
NEXT_SYS_IDLE=$(echo $NEXT_CPU_INFO | awk '{print $4}')
NEXT_TOTAL_CPU_T=$(echo $NEXT_CPU_INFO | awk '{print $1+$2+$3+$4+$5+$6+$7}')

#系统空闲时间
SYSTEM_IDLE=`echo ${NEXT_SYS_IDLE} ${LAST_SYS_IDLE} | awk '{print $1-$2}'`
#CPU总时间
TOTAL_TIME=`echo ${NEXT_TOTAL_CPU_T} ${LAST_TOTAL_CPU_T} | awk '{print $1-$2}'`
CPU_USAGE=`echo ${SYSTEM_IDLE} ${TOTAL_TIME} | awk '{printf "%.2f", 100-$1/$2*100}'`

#向文件中输出结果
echo "Report Time:" $time >>/home/cpu_monitor.txt
echo "CPU Manufacturers:"`cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c` >>/home/cpu_monitor.txt
echo "CPU Number:" `cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l` >>/home/cpu_monitor.txt
echo -e "CPU Usage:${CPU_USAGE}%\n"  >>/home/cpu_monitor.txt

执行结果:

向文件中输出命令执行的错误信息同样可以参考上面的方法,不同的地方是需要在>>>前面加上2,注意不要有空格。

(2)/dev/null 文件

在Unix系统中,/dev/null,或称空设备,是一个特殊的设备文件,它丢弃一切写入其中的数据(但报告写入操作成功),读取它则会立即得到一个EOF。在程序员行话,尤其是Unix行话中,/dev/null 被称为位桶(bit bucket)或者黑洞(black hole)。空设备通常被用于丢弃不需要的输出流,或作为用于输入流的空文件。当你读它的时候,它会提供无限的空字符(NULL, ASCII NUL, 0x00)。

因此利用这个文件,如果你既不想把命令的输出结果保存到文件,也不想把命令的输出结果显示到屏幕上,干扰命令的执行,那么可以把命令的所有结果重定向到 /dev/null 文件中。如下所示:

#在这里我们并不关心ping的过程,只关心是否能ping通,因此把ping的结果从定向到/dev/null文件中
if [ ping c1 www.baidu.com &>/dev/null ]; then
   echo "network is working!"
else
   echo "netwoek is not working"
fi   

关于/dev/null的用处还用很多,这里只是举一个例子,在使用的时候大家可以把 /dev/null 当成 Linux 系统的垃圾箱,任何放入垃圾箱的数据都会被丢弃,不能恢复。

三、Linux Shell输入重定向

输入重定向就是改变输入的方向,不再使用键盘作为命令输入的来源,而是使用文件作为命令的输入。

符号 说明
command <file 将 file 文件中的内容作为 command 的输入。
command <<END 从标准输入(键盘)中读取数据,直到遇见分界符 END 才停止(分界符可以是任意的字符串,用户自己定义)。
command <file1 >file2 将 file1 作为 command 的输入,并将 command 的处理结果输出到 file2。

和输出重定向类似,输入重定向的完整写法是fd<file,其中 fd 表示文件描述符,如果不写,默认为 0,也就是标准输入文件。

案例

(1)统计给定文本中有多少行

统计刚才CPU监控脚本定时计划向/home/cpu_monitor.txt文本中输出的文本的行数

(2)逐行读取文件

读取/etc/scripts/numbers.txt文件(每个数字占一行),读取到脚本中对文件中的数字排序

#!/bin/bash
read -p "Please input number text path: " path
if [ -n "$path" -a ! -f "$path" ]; then
  echo "$path: can not find"
  exit 1
fi

#获取文件中的中数据
while read line; do
    numbers[i++]=$number
done

#排序:使用冒泡排序
for((i=0;i<$[${#numbers[@]}-1];i++)); do
    for((j=0;j<$[${#numbers[@]}-${i}-1];j++)); do
        moved=0
        if [ "${numbers[j]}" -gt "${numbers[j+1]}" ]; then
            temp=${numbers[j]}
            numbers[j]=${numbers[j+1]}
            numbers[j+1]=$temp
            moved=1
        fi
    done
    #moved 为0 说明此趟没有发生元素的移动,即元素已经拍好序了,那就直接跳出循环
    if [ "$moved" -eq 0 ]; then
       break
    fi
done

#把数字输出到path中
cp $path ${path}.bak
echo  ${numbers[@]}>$path

echo -e "\nComplete!"

执行结果:

(3)Here Document

Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。

它的基本的形式如下:

command << delimiter
  document
delimiter

它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。

留言区

还能输入500个字符