1. Shell 重定向
1.1 Shell 重定向是什么
我們在之前章節有學習 echo/printf 來將我們的需求輸出,此時就是我們將系統的返回輸出到我們標準終端,使得我們能夠看到正常的輸出的結果,Unix 命令默認的輸入設備即 stdin
為鍵盤,標準和錯誤設備即 stdout
為顯示器,我們利用重定向可以將輸入改為文件,或者將輸出重新定向到其他設備或文件中。
1.2 為什么要用重定向
我們知道了系統默認的輸入為鍵盤,標準輸出與錯誤輸出為顯示器,當我們在編寫 Shell 的時候有一些非交互的操作,不能通過鍵盤輸入,或顯示的結果我們不希望在顯示器顯示的時候,此場景就需要利用輸入輸出重定向了。
2. Shell 輸入輸出重定向
Linux Shell 重定向分為兩種,顧名思義,輸入重定向即改變標準的默認系統鍵盤輸入,輸出重定向即改變默認的系統顯示器輸出。
2.1 文件描述符
在 Linux 中一切皆文件,包括標準輸入設備(鍵盤)和標準輸出設備(顯示器)在內的所有計算機硬件都是文件。為了表示和區分已經打開的文件,Linux 會給每個文件分配一個 ID,這個 ID 就是一個整數,被稱為文件描述符(File Descriptor)。
如下是文件描述符的類型及其對應的設備。
文件描述符 | 文件名 | 類型 | 硬件 |
---|---|---|---|
0 | stdin | 標準輸入文件 | 鍵盤 |
1 | stdout | 標準輸出文件 | 顯示器 |
2 | stderr | 標準錯誤輸出文件 | 顯示器 |
Linux 程序在你執行任何形式的 I/O 操作時,其實都是在對一個文件描述符進行讀取或寫入,一個文件描述符只是一個打開的文件相關聯的整數,在其背后就是硬盤上一個普通文件或管道,鍵盤,顯示器,或是一個網絡鏈接等。

如圖更為形象的展示鍵盤是 Linux 系統默認標準輸入設備,當然可以重定向為 file,對應的命令執行的標準輸出與標準錯誤輸出設備為屏幕,也可以根據需求重定向到文件。
2.2 輸入重定向
輸入方向為數據從那流入程序,輸入重定向即改變默認的系統鍵盤輸入,改變其從其他對方流入程序。
2.2.1 <
command <file,將 file 文件中的內容作為 command 的輸入。
格式:
[n]< word
注意 [n] 與 < 之間沒有空格,其中將文件描述符 n 重定向到 word 指代的文件(以只讀方式打開), 如果不顯示指明 n,默認就為 0,標準輸入,例如:
[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
文件內容為 test content
,在輸入重定向時,我們將文件描述符 0 重定向到 testfile.txt
,所以利用命令 cat 查看,結果就為文件的內容,默認就是標準輸入,所以可以不寫 0。
[root@xuel-terraform-cvm-0 ~]# 0< testfile.txt cat
test content
[root@xuel-terraform-cvm-0 ~]# < testfile.txt cat
test content
解析器解析到 “<” 以后會先處理重定向,將標準輸入重定向到 file,之后 cat 再從標準輸入讀取指令的時候,由于標準輸入已經重定向到了 file ,于是 cat 就從 file 中讀取指令了。
2.2.2 <<EOF
command <<END,從標準輸入(鍵盤)中讀取數據,直到遇見分界符 END 才停止,分界符可以是自定義的任意字符,在此建議使用 EOF。
該輸入重定向可以很方便用于批量文件的輸入,可以用此來創建文件,例如:
[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 來從標準輸入中讀取數據,直到遇到結束標示 EOF 停止。
例如我們在學習流程控制中的 while 循環讀取文件就利用了輸入重定向,例如:
[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 來逐行讀取文件中的內容。
2.3 輸出重定向
輸出方向為數據輸出到那個終端,輸出重定向即改變默認的顯示器輸出,改變其從其他設備輸出。
一般輸出重定向的應用場景多為將標準輸出或標準錯誤輸出分別保持到不同的文件,或者是我們不關心輸出等情況等。
如下整理的標準輸出重定向與標準錯誤輸出重定向:
2.3.1 標準輸出重定向
- 覆蓋方式
語法:command >file
標準輸入重定向覆蓋方式,直接將 command 命令的標準輸出,以覆蓋方式輸出到文件中,例如:
[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
可以看到將文件的原始內容已經覆蓋掉了,也可以用來清空文件內容,例如:
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
test
[root@xuel-terraform-cvm-0 ~]# >file1.txt
[root@xuel-terraform-cvm-0 ~]# cat file1.txt
- 追加方式
語法:command >>file
將標準的輸出追加到文件中,注意追加為不覆蓋原始文件內容,例如:
[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 錯誤輸出重定向
- 覆蓋方式:
語法:command 2>file
與標準輸出重定向一樣,只是綁定標準錯誤輸出文件描述符 2,例如:
[root@xuel-terraform-cvm-0 ~]# ls /none
ls: 無法訪問/none: 沒有那個文件或目錄
[root@xuel-terraform-cvm-0 ~]# ls /none 2> error.txt
[root@xuel-terraform-cvm-0 ~]# cat error.txt
ls: 無法訪問/none: 沒有那個文件或目錄
我們可以使用 ls
查看一個不存在的文件或目錄,會輸出標準錯誤輸出,將其重定向到 error.txt 中。
- 追加方式:
語法:command 2>>file
與標準輸出追加方式一樣,只是綁定標準錯誤輸出文件描述符,例如:
[root@xuel-terraform-cvm-0 ~]# abc 2>>error.txt
[root@xuel-terraform-cvm-0 ~]# cat error.txt
ls: 無法訪問/none: 沒有那個文件或目錄
-bash: abc: command not found
我們使用命令 abc,Shell 提示我們沒有這個命令,在此就將標準錯誤輸出以追加形式重定向到文件中。
2.3.3 全部重定向
在我們使用輸出重定向分為標準輸出與錯誤輸出,當我們希望將兩者都重定向到某文件使用可以使用 &>
,例如:
[root@xuel-terraform-cvm-0 ~]# cat totle.txt
ls: 無法訪問/none: 沒有那個文件或目錄
/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
我們可以看出無論標準輸出或錯誤輸出都重定向到了 totle.txt
文件中。
2.4 組合使用
輸入和輸出也是可以組合使用的,那么這個組合主要應用于在 Shell 腳本當中產生新的配置文件的場景下,例如:
[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
在此就組合標準輸出重定向與輸入重定向組合使用。
2.5 空設備
在 Linux 系統中存在一個空設備,也稱為黑洞設備,其為 /dev/null
。當我們將內容重定向到它時會被丟棄,對其也無法進行讀取內容操作,利用它,可以在我們編寫 Shell 中能夠起到禁止異常輸出的功效,例如:
[root@xuel-terraform-cvm-0 ~]# ls / /none
ls: 無法訪問/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-terraform-cvm-0 ~]# ls / /none >/dev/null 2>&1
[root@xuel-terraform-cvm-0 ~]# ls / /none &>/dev/null
可以看到我們先將標準輸出重定向到 /dev/null
,對于錯誤標準輸出全部又重定向到標準輸出,從而達到了將全部輸出禁止掉。
或者我們使用 &
將標準輸出與標準錯誤輸出全部重定向到 /dev/null
,同樣能達到禁止輸出的效果。
3. 實例
3.1 需求
編寫一個腳本,獲取 Linux 系統的 CPU 和內存信息,然后輸出到文件中。
3.2 思路
可以利用函數來分別編寫 CPU 和內存信息,最后利用重定向將信息輸出到文件中。
3.3 實現
#!/bin/bash
# Description: sys check
# Auth: kaliarch
# Email: [email protected]
# 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"
# 獲取系統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個數: $Physical_CPUs
邏輯CPU個數: $Virt_CPUs
每CPU核心數: $CPU_Kernels
CPU型號: $CPU_Type
CPU架構: $CPU_Arch
EOF
}
# 獲取系統內存信息
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" #內存總容量(MB)
report_MemFree="$((MemFree/1024))""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_cpu_info
echo ${line}
get_mem_info
echo ${line}
}
# 執行主函數將輸出重定向到文件中
sys_check > ${sys_check_file}
# 執行測試
[root@xuel-terraform-cvm-0 ~]# bash sys_check.sh
[root@xuel-terraform-cvm-0 ~]# cat logs/10.0.1.15-20200329.txt
CPU信息:
物理CPU個數: 1
邏輯CPU個數: 1
每CPU核心數: 1
CPU型號: Intel(R) Xeon(R) CPU E5-26xx v4
CPU架構: 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. 小結
對于重定向特別適用于文件的操作或者某些不關注輸出結果的場景,在本章節需要注意覆蓋模式與追加模式的區別,靈活配合空設備對特定場景進行應用,理解三個文件描述符,百變不離其中靈活組合使用。