Shell 输入输出重定向
1. Shell
我们在之前章节有学习 echo/printf 来将我们的需求,此时就是我们将系统的返回到我们标准终端,使得我们能够看到正常的的结果,Unix 命令认的输入设备即 stdin
为,标准和设备即 stdout
为器,我们利用可以将输入改为,或者将重新定向到其他设备或中。
我们知道了系统认的输入为,标准与为器,当我们在编写 Shell 的时候有一些非交互的操作,不能通过输入,或的结果我们不希望在器的时候,此场景就需要利用输入了。
2. Shell 输入
Linux Shell 分为两种,顾名思义,输入即改变标准的认系统输入,即改变认的系统器。
在 Linux 中一切皆,标准输入设备()和标准设备(器)在内的所有计算机硬件都是。为了表示和区分已经打开的,Linux 会给每个分配 ID,这个 ID 就是整数,被称为描述符(File Descriptor)。
如下是描述符的类型及其对应的设备。
Linux 程序在你执行任何形式的 操作时,其实都是在对描述符进行读取或写入,描述符只是打开的相关联的整数,在其背后就是硬盘上普通或管道,,器,或是网络等。
如图更为形象的展示是 Linux 系统认标准输入设备,当然可以为 file,对应的命令执行的标准与标准设备为屏幕,也可以根据需求到。
输入方向为数据从那流入程序,输入即改变认的系统输入,改变其从其他对方流入程序。
command <file,将 file 中的作为 command 的输入。
格式:
[n]< word
注意 [n] 与 < 之间没有空格,其中将描述符 n 到 word 指代的(以只读方式打开), 如果指明 n,认就为 0,标准输入,例如:
[root@xuel--cvm-0 ~]# cat testfile.txttest content[root@xuel--cvm-0 ~]# cat 0< testfile.txttest content[root@xuel--cvm-0 ~]# cat < testfile.txttest content
我们可以看到 testfile.txt
为 test content
,在输入时,我们将描述符 0 到 testfile.txt
,所以利用命令 cat 查看,结果就为的,认就是标准输入,所以可以不写 0。
[root@xuel--cvm-0 ~]# 0< testfile.txt cattest content[root@xuel--cvm-0 ~]# < testfile.txt cattest content
解析器解析到 “<” 以后会先处理,将标准输入到 file,之后 cat 再从标准输入读取指令的时候,由于标准输入已经到了 file ,于是 cat 就从 file 中读取指令了。
command <<END,从标准输入()中读取数据,直到遇见分界符 END 才停止,分界符可以是的任意字符,在此建议使用 EOF。
该输入可以很方便用于批量的输入,可以用此来,例如:
[root@xuel--cvm-0 ~]# cat > file1.txt <<EOF> hello shell> hello go> test file> EOF[root@xuel--cvm-0 ~]# cat file1.txthello shell hello gotest file
在此利用了将 cat 的到 file1.txt
中,之后利用 <<EOF 来从标准输入中读取数据,直到遇到结束标示 EOF 停止。
例如我们在学习流程控制中的 while 循环读取就利用了输入,例如:
[root@xuel--cvm-0 ~]# cat while.sh#!/bin/bashFILE=file1.txtwhile read str; doecho $strdone <$FILE[root@xuel--cvm-0 ~]# bash while.shhello shell hello gotest file
在此将绑定到输入上,利用 while 来逐行读取中的。
方向为数据到那个终端,即改变认的器,改变其从其他设备。
一般的应用场景多为将标准或标准分别保持到不同的,或者是我们不关心等情况等。
如下整理的标准与标准:
覆盖方式
语法:command >file
标准输入覆盖方式,直接将 command 命令的标准,以覆盖方式到中,例如:
[root@xuel--cvm-0 ~]# cat file1.txthello shell hello gotest file[root@xuel--cvm-0 ~]# echo "test" > file1.txt[root@xuel--cvm-0 ~]# cat file1.txttest
可以看到将的原始已经覆盖掉了,也可以用来清空,例如:
[root@xuel--cvm-0 ~]# cat file1.txttest[root@xuel--cvm-0 ~]# >file1.txt[root@xuel--cvm-0 ~]# cat file1.txt
追加方式
语法:command >>file
将标准的追加到中,注意追加为不覆盖原始,例如:
[root@xuel--cvm-0 ~]# cat file1.txttest[root@xuel--cvm-0 ~]# echo "test222" >> file1.txt[root@xuel--cvm-0 ~]# cat file1.txttesttest222
覆盖方式:
语法:command 2>file
与标准一样,只是绑定标准描述符 2,例如:
[root@xuel--cvm-0 ~]# ls /nonels: 无法访问/none: 没有那个或目录[root@xuel--cvm-0 ~]# ls /none 2> error.txt[root@xuel--cvm-0 ~]# cat error.txtls: 无法访问/none: 没有那个或目录
我们可以使用 ls
查看不存在的或目录,会标准,将其到 error.txt 中。
追加方式:
语法:command 2>>file
与标准追加方式一样,只是绑定标准描述符,例如:
[root@xuel--cvm-0 ~]# abc 2>>error.txt[root@xuel--cvm-0 ~]# cat error.txtls: 无法访问/none: 没有那个或目录 -bash: abc: command not found
我们使用命令 abc,Shell 我们没有这个命令,在此就将标准以追加形式到中。
在我们使用分为标准与,当我们希望将两者都到某使用可以使用 &>
,例如:
[root@xuel--cvm-0 ~]# cat totle.txt ls: 无法访问/none: 没有那个或目录 /tmp: limit-0.2 cvm_init.log net_affinity.log nohup.out nv_driver_install.log nv_gpu_conf.log setRps.log v0.2.tar.gz virtio_blk_affinity.log
我们可以看出无论标准或都到了 totle.txt
中。
输入和也是可以组合使用的,那么这个组合主要应用于在 Shell 脚本当中产生新的的场景下,例如:
[root@xuel--cvm-0 ~]# cat > file1.txt <<EOF> hello shell> hello go> test file> EOF[root@xuel--cvm-0 ~]# cat file1.txthello shell hello gotest file
在此就组合标准与输入组合使用。
在 Linux 系统中存在空设备,也称为黑洞设备,其为 /dev/null
。当我们将到它时会被丢弃,对其也无法进行读取操作,利用它,可以在我们编写 Shell 中能够起到异常的功效,例如:
[root@xuel--cvm-0 ~]# ls / /nonels: 无法访问/none: 没有那个或目录 /: bin boot data dev etc home lib lib64 lost+found media mnt opt proc root run sbin selinux srv sys tmp usr var[root@xuel--cvm-0 ~]# ls / /none >/dev/null 2>&1[root@xuel--cvm-0 ~]# ls / /none &>/dev/null
可以看到我们先将标准到 /dev/null
,对于标准全部又到标准,从而达到了将全部掉。
或者我们使用 &
将标准与标准全部到 /dev/null
,同样能达到的。
3. 实例
编写脚本, Linux 系统的 和,然后到中。
可以利用来分别编写 和,最后利用将信息到中。
#!/bin/bash# Description: sys check# Auth: kaliarch# Email: kaliarch@163.com# function: sys check# Date: 2020-03-29 14:00# Version: 1.0[ $(id -u) -gt 0 ] && echo "请用root执行此脚本!" && exit 1 sysversion=$(rpm -q centos-release|cut -d- -f3)line="-------------------------------------------------"[ -d logs ] || mkdir logs sys_check_file="logs/$(ip a show dev eth0|grep -w inet|awk '{print $2}'|awk -F '/' '{print $1}')-`date +%Y%m%d`.txt"# 系统信息function get__info() {Physical_s=$(grep "physical id" /proc/info| sort | uniq | wc -l)Virt_s=$(grep "processor" /proc/info | wc -l)_Kernels=$(grep "cores" /proc/info|uniq| awk -F ': ' '{print $2}')_Type=$(grep "model name" /proc/info | awk -F ': ' '{print $2}' | sort | uniq)_Arch=$(uname -m)cat <<EOF | column -t 信息: 物理个数: $Physical_s逻辑个数: $Virt_s每核心数: $_Kernels型号: $_Type架构: $_ArchEOF}# 系统function get_mem_info() {check_mem=$(free -m)MemTotal=$(grep MemTotal /proc/meminfo| awk '{print $2}') #KBMemFree=$(grep MemFree /proc/meminfo| awk '{print $2}') #KBlet MemUsed=MemTotal-MemFree MemPercent=$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}")report_MemTotal="$((MemTotal/))""MB" #内存总容量(MB)report_MemFree="$((MemFree/))""MB" #内存剩余(MB)report_MemUsedPercent="$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}")""%" #内存使用率%cat <<EOF :${check_mem}EOF}# 定义主function sys_check() {get__infoecho ${line}get_mem_infoecho ${line}}# 执行主将到中sys_check > ${sys_check_file}# 执行测试[root@xuel--cvm-0 ~]# bash sys_check.sh[root@xuel--cvm-0 ~]# cat logs/10.0.1.15-20200329.txt信息: 物理个数: 1 逻辑个数: 1 每核心数: 1 型号: Intel(R) Xeon(R) E5-26xx v4 架构: x86_64 ------------------------------------------------- : total used free shared buffers cached Mem: 996 920 76 0 191 600 -/+ buffers/cache: 127 868 Swap: 0 0 0 -------------------------------------------------
可以看到利用了两个来系统的信息,将其利用方式到中。
4. 注意事项
一条 shell 命令,都会继承其父进程的描述符,因此所有的 shell 命令,都会认有三个描述符;
所有输入都是由该进程所有打开的描述符控制的,Linux 一切皆,因此他们的输入也是由描述符控制;
在 <
中分界符可以是的任意字符,在此建议使用 EOF;
在进行追加的时候可以使用追加方式,切记操作前备份老,以免异常。
5. 小结
对于特别适用于的操作或者某些不关注结果的场景,在本章节需要注意覆盖模式与追加模式的区别,灵活配合空设备对特定场景进行应用,理解三个描述符,百变不离其中灵活组合使用。