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。