1. Shell 重定向
1.1 Shell 重定向是什么
我們?cè)谥罢鹿?jié)有學(xué)習(xí) echo/printf 來(lái)將我們的需求輸出,此時(shí)就是我們將系統(tǒng)的返回輸出到我們標(biāo)準(zhǔn)終端,使得我們能夠看到正常的輸出的結(jié)果,Unix 命令默認(rèn)的輸入設(shè)備即 stdin
為鍵盤(pán),標(biāo)準(zhǔn)和錯(cuò)誤設(shè)備即 stdout
為顯示器,我們利用重定向可以將輸入改為文件,或者將輸出重新定向到其他設(shè)備或文件中。
1.2 為什么要用重定向
我們知道了系統(tǒng)默認(rèn)的輸入為鍵盤(pán),標(biāo)準(zhǔn)輸出與錯(cuò)誤輸出為顯示器,當(dāng)我們?cè)诰帉?xiě) Shell 的時(shí)候有一些非交互的操作,不能通過(guò)鍵盤(pán)輸入,或顯示的結(jié)果我們不希望在顯示器顯示的時(shí)候,此場(chǎng)景就需要利用輸入輸出重定向了。
2. Shell 輸入輸出重定向
Linux Shell 重定向分為兩種,顧名思義,輸入重定向即改變標(biāo)準(zhǔn)的默認(rèn)系統(tǒng)鍵盤(pán)輸入,輸出重定向即改變默認(rèn)的系統(tǒng)顯示器輸出。
2.1 文件描述符
在 Linux 中一切皆文件,包括標(biāo)準(zhǔn)輸入設(shè)備(鍵盤(pán))和標(biāo)準(zhǔn)輸出設(shè)備(顯示器)在內(nèi)的所有計(jì)算機(jī)硬件都是文件。為了表示和區(qū)分已經(jīng)打開(kāi)的文件,Linux 會(huì)給每個(gè)文件分配一個(gè) ID,這個(gè) ID 就是一個(gè)整數(shù),被稱(chēng)為文件描述符(File Descriptor)。
如下是文件描述符的類(lèi)型及其對(duì)應(yīng)的設(shè)備。
文件描述符 | 文件名 | 類(lèi)型 | 硬件 |
---|---|---|---|
0 | stdin | 標(biāo)準(zhǔn)輸入文件 | 鍵盤(pán) |
1 | stdout | 標(biāo)準(zhǔn)輸出文件 | 顯示器 |
2 | stderr | 標(biāo)準(zhǔn)錯(cuò)誤輸出文件 | 顯示器 |
Linux 程序在你執(zhí)行任何形式的 I/O 操作時(shí),其實(shí)都是在對(duì)一個(gè)文件描述符進(jìn)行讀取或?qū)懭耄粋€(gè)文件描述符只是一個(gè)打開(kāi)的文件相關(guān)聯(lián)的整數(shù),在其背后就是硬盤(pán)上一個(gè)普通文件或管道,鍵盤(pán),顯示器,或是一個(gè)網(wǎng)絡(luò)鏈接等。

如圖更為形象的展示鍵盤(pán)是 Linux 系統(tǒng)默認(rèn)標(biāo)準(zhǔn)輸入設(shè)備,當(dāng)然可以重定向?yàn)?file,對(duì)應(yīng)的命令執(zhí)行的標(biāo)準(zhǔn)輸出與標(biāo)準(zhǔn)錯(cuò)誤輸出設(shè)備為屏幕,也可以根據(jù)需求重定向到文件。
2.2 輸入重定向
輸入方向?yàn)閿?shù)據(jù)從那流入程序,輸入重定向即改變默認(rèn)的系統(tǒng)鍵盤(pán)輸入,改變其從其他對(duì)方流入程序。
2.2.1 <
command <file,將 file 文件中的內(nèi)容作為 command 的輸入。
格式:
[n]< word
注意 [n] 與 < 之間沒(méi)有空格,其中將文件描述符 n 重定向到 word 指代的文件(以只讀方式打開(kāi)), 如果不顯示指明 n,默認(rèn)就為 0,標(biāo)準(zhǔn)輸入,例如:
[root@xuel-terraform-cvm-0 ~]# cat testfile.txt
test content
[root@xuel-terraform-cvm-0 ~]# cat 0< testfile.txt
test content
[root@xuel-terraform-cvm-0 ~]# cat < testfile.txt
test content
我們可以看到 testfile.txt
文件內(nèi)容為 test content
,在輸入重定向時(shí),我們將文件描述符 0 重定向到 testfile.txt
,所以利用命令 cat 查看,結(jié)果就為文件的內(nèi)容,默認(rèn)就是標(biāo)準(zhǔn)輸入,所以可以不寫(xiě) 0。
[root@xuel-terraform-cvm-0 ~]# 0< testfile.txt cat
test content
[root@xuel-terraform-cvm-0 ~]# < testfile.txt cat
test content
解析器解析到 “<” 以后會(huì)先處理重定向,將標(biāo)準(zhǔn)輸入重定向到 file,之后 cat 再?gòu)臉?biāo)準(zhǔn)輸入讀取指令的時(shí)候,由于標(biāo)準(zhǔn)輸入已經(jīng)重定向到了 file ,于是 cat 就從 file 中讀取指令了。
2.2.2 <<EOF
command <<END,從標(biāo)準(zhǔn)輸入(鍵盤(pán))中讀取數(shù)據(jù),直到遇見(jiàn)分界符 END 才停止,分界符可以是自定義的任意字符,在此建議使用 EOF。
該輸入重定向可以很方便用于批量文件的輸入,可以用此來(lái)創(chuàng)建文件,例如:
[root@xuel-terraform-cvm-0 ~]# cat > file1.txt <<EOF
> hello shell
> hello go
> test file
> EOF
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
hello shell
hello go
test file
在此利用了將 cat 的輸出重定向到文件 file1.txt
中,之后利用 <<EOF 來(lái)從標(biāo)準(zhǔn)輸入中讀取數(shù)據(jù),直到遇到結(jié)束標(biāo)示 EOF 停止。
例如我們?cè)趯W(xué)習(xí)流程控制中的 while 循環(huán)讀取文件就利用了輸入重定向,例如:
[root@xuel-terraform-cvm-0 ~]# cat while.sh
#!/bin/bash
FILE=file1.txt
while read str; do
echo $str
done <$FILE
[root@xuel-terraform-cvm-0 ~]# bash while.sh
hello shell
hello go
test file
在此將文件綁定到輸入重定向上,利用 while 來(lái)逐行讀取文件中的內(nèi)容。
2.3 輸出重定向
輸出方向?yàn)閿?shù)據(jù)輸出到那個(gè)終端,輸出重定向即改變默認(rèn)的顯示器輸出,改變其從其他設(shè)備輸出。
一般輸出重定向的應(yīng)用場(chǎng)景多為將標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯(cuò)誤輸出分別保持到不同的文件,或者是我們不關(guān)心輸出等情況等。
如下整理的標(biāo)準(zhǔn)輸出重定向與標(biāo)準(zhǔn)錯(cuò)誤輸出重定向:
2.3.1 標(biāo)準(zhǔn)輸出重定向
- 覆蓋方式
語(yǔ)法:command >file
標(biāo)準(zhǔn)輸入重定向覆蓋方式,直接將 command 命令的標(biāo)準(zhǔn)輸出,以覆蓋方式輸出到文件中,例如:
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
hello shell
hello go
test file
[root@xuel-terraform-cvm-0 ~]# echo "test" > file1.txt
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
test
可以看到將文件的原始內(nèi)容已經(jīng)覆蓋掉了,也可以用來(lái)清空文件內(nèi)容,例如:
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
test
[root@xuel-terraform-cvm-0 ~]# >file1.txt
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
- 追加方式
語(yǔ)法:command >>file
將標(biāo)準(zhǔn)的輸出追加到文件中,注意追加為不覆蓋原始文件內(nèi)容,例如:
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
test
[root@xuel-terraform-cvm-0 ~]# echo "test222" >> file1.txt
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
test
test222
2.3.2 錯(cuò)誤輸出重定向
- 覆蓋方式:
語(yǔ)法:command 2>file
與標(biāo)準(zhǔn)輸出重定向一樣,只是綁定標(biāo)準(zhǔn)錯(cuò)誤輸出文件描述符 2,例如:
[root@xuel-terraform-cvm-0 ~]# ls /none
ls: 無(wú)法訪問(wèn)/none: 沒(méi)有那個(gè)文件或目錄
[root@xuel-terraform-cvm-0 ~]# ls /none 2> error.txt
[root@xuel-terraform-cvm-0 ~]# cat error.txt
ls: 無(wú)法訪問(wèn)/none: 沒(méi)有那個(gè)文件或目錄
我們可以使用 ls
查看一個(gè)不存在的文件或目錄,會(huì)輸出標(biāo)準(zhǔn)錯(cuò)誤輸出,將其重定向到 error.txt 中。
- 追加方式:
語(yǔ)法:command 2>>file
與標(biāo)準(zhǔn)輸出追加方式一樣,只是綁定標(biāo)準(zhǔn)錯(cuò)誤輸出文件描述符,例如:
[root@xuel-terraform-cvm-0 ~]# abc 2>>error.txt
[root@xuel-terraform-cvm-0 ~]# cat error.txt
ls: 無(wú)法訪問(wèn)/none: 沒(méi)有那個(gè)文件或目錄
-bash: abc: command not found
我們使用命令 abc,Shell 提示我們沒(méi)有這個(gè)命令,在此就將標(biāo)準(zhǔn)錯(cuò)誤輸出以追加形式重定向到文件中。
2.3.3 全部重定向
在我們使用輸出重定向分為標(biāo)準(zhǔn)輸出與錯(cuò)誤輸出,當(dāng)我們希望將兩者都重定向到某文件使用可以使用 &>
,例如:
[root@xuel-terraform-cvm-0 ~]# cat totle.txt
ls: 無(wú)法訪問(wèn)/none: 沒(méi)有那個(gè)文件或目錄
/tmp:
cpulimit-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
我們可以看出無(wú)論標(biāo)準(zhǔn)輸出或錯(cuò)誤輸出都重定向到了 totle.txt
文件中。
2.4 組合使用
輸入和輸出也是可以組合使用的,那么這個(gè)組合主要應(yīng)用于在 Shell 腳本當(dāng)中產(chǎn)生新的配置文件的場(chǎng)景下,例如:
[root@xuel-terraform-cvm-0 ~]# cat > file1.txt <<EOF
> hello shell
> hello go
> test file
> EOF
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
hello shell
hello go
test file
在此就組合標(biāo)準(zhǔn)輸出重定向與輸入重定向組合使用。
2.5 空設(shè)備
在 Linux 系統(tǒng)中存在一個(gè)空設(shè)備,也稱(chēng)為黑洞設(shè)備,其為 /dev/null
。當(dāng)我們將內(nèi)容重定向到它時(shí)會(huì)被丟棄,對(duì)其也無(wú)法進(jìn)行讀取內(nèi)容操作,利用它,可以在我們編寫(xiě) Shell 中能夠起到禁止異常輸出的功效,例如:
[root@xuel-terraform-cvm-0 ~]# ls / /none
ls: 無(wú)法訪問(wèn)/none: 沒(méi)有那個(gè)文件或目錄
/:
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-terraform-cvm-0 ~]# ls / /none >/dev/null 2>&1
[root@xuel-terraform-cvm-0 ~]# ls / /none &>/dev/null
可以看到我們先將標(biāo)準(zhǔn)輸出重定向到 /dev/null
,對(duì)于錯(cuò)誤標(biāo)準(zhǔn)輸出全部又重定向到標(biāo)準(zhǔn)輸出,從而達(dá)到了將全部輸出禁止掉。
或者我們使用 &
將標(biāo)準(zhǔn)輸出與標(biāo)準(zhǔn)錯(cuò)誤輸出全部重定向到 /dev/null
,同樣能達(dá)到禁止輸出的效果。
3. 實(shí)例
3.1 需求
編寫(xiě)一個(gè)腳本,獲取 Linux 系統(tǒng)的 CPU 和內(nèi)存信息,然后輸出到文件中。
3.2 思路
可以利用函數(shù)來(lái)分別編寫(xiě) CPU 和內(nèi)存信息,最后利用重定向?qū)⑿畔⑤敵龅轿募小?/p>
3.3 實(shí)現(xiàn)
#!/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 "請(qǐng)用root用戶(hù)執(zhí)行此腳本!" && 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"
# 獲取系統(tǒng)cpu信息
function get_cpu_info() {
Physical_CPUs=$(grep "physical id" /proc/cpuinfo| sort | uniq | wc -l)
Virt_CPUs=$(grep "processor" /proc/cpuinfo | wc -l)
CPU_Kernels=$(grep "cores" /proc/cpuinfo|uniq| awk -F ': ' '{print $2}')
CPU_Type=$(grep "model name" /proc/cpuinfo | awk -F ': ' '{print $2}' | sort | uniq)
CPU_Arch=$(uname -m)
cat <<EOF | column -t
CPU信息:
物理CPU個(gè)數(shù): $Physical_CPUs
邏輯CPU個(gè)數(shù): $Virt_CPUs
每CPU核心數(shù): $CPU_Kernels
CPU型號(hào): $CPU_Type
CPU架構(gòu): $CPU_Arch
EOF
}
# 獲取系統(tǒng)內(nèi)存信息
function get_mem_info() {
check_mem=$(free -m)
MemTotal=$(grep MemTotal /proc/meminfo| awk '{print $2}') #KB
MemFree=$(grep MemFree /proc/meminfo| awk '{print $2}') #KB
let MemUsed=MemTotal-MemFree
MemPercent=$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}")
report_MemTotal="$((MemTotal/1024))""MB" #內(nèi)存總?cè)萘?MB)
report_MemFree="$((MemFree/1024))""MB" #內(nèi)存剩余(MB)
report_MemUsedPercent="$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}")""%" #內(nèi)存使用率%
cat <<EOF
內(nèi)存信息:
${check_mem}
EOF
}
# 定義主函數(shù)
function sys_check() {
get_cpu_info
echo ${line}
get_mem_info
echo ${line}
}
# 執(zhí)行主函數(shù)將輸出重定向到文件中
sys_check > ${sys_check_file}
# 執(zhí)行測(cè)試
[root@xuel-terraform-cvm-0 ~]# bash sys_check.sh
[root@xuel-terraform-cvm-0 ~]# cat logs/10.0.1.15-20200329.txt
CPU信息:
物理CPU個(gè)數(shù): 1
邏輯CPU個(gè)數(shù): 1
每CPU核心數(shù): 1
CPU型號(hào): Intel(R) Xeon(R) CPU E5-26xx v4
CPU架構(gòu): x86_64
-------------------------------------------------
內(nèi)存信息:
total used free shared buffers cached
Mem: 996 920 76 0 191 600
-/+ buffers/cache: 127 868
Swap: 0 0 0
-------------------------------------------------
可以看到利用了兩個(gè)函數(shù)來(lái)獲取系統(tǒng)的信息,將其利用重定向方式輸出到文件中。
4. 注意事項(xiàng)
- 一條 shell 命令,都會(huì)繼承其父進(jìn)程的文件描述符,因此所有的 shell 命令,都會(huì)默認(rèn)有三個(gè)文件描述符;
- 文件所有輸入輸出都是由該進(jìn)程所有打開(kāi)的文件描述符控制的,Linux 一切皆文件,因此他們的輸入輸出也是由文件描述符控制;
- 在
<
中分界符可以是自定義的任意字符,在此建議使用 EOF; - 在進(jìn)行文件追加的時(shí)候可以使用追加方式重定向,切記操作文件前備份老文件,以免文件內(nèi)容異常。
5. 小結(jié)
對(duì)于重定向特別適用于文件的操作或者某些不關(guān)注輸出結(jié)果的場(chǎng)景,在本章節(jié)需要注意覆蓋模式與追加模式的區(qū)別,靈活配合空設(shè)備對(duì)特定場(chǎng)景進(jìn)行應(yīng)用,理解三個(gè)文件描述符,百變不離其中靈活組合使用。