Shell 命令輸入、輸出
Shell 的輸入、輸出方式有兩種(大方向):1. 輸出至螢幕、2. 輸出至檔案中;而這個輸入輸出是可以控制的,了解 Linux 輸入、輸出可以協助我們控制
描述符:標準文件的代號
● 在 Linux 系統中,「文件」通常被視為一個 檔案描述符(file descriptor
),它是一個用於識別打開文件的整數值;當應用程序需要讀取或寫入文件時,它會通過文件描述符與文件進行交互
● 其實 Linux 不只將文件視為文件,它也將進程、設被、Socket 視為文件
● 多進程 & 檔案描述符?
多進程是一種應用程序的設計模式(並行範式),它可以讓一個應用程序同時運行多個獨立的進程(會有父子進程的概念)
在這些進程之間可以通過進程間通訊(IPC)機制進行通信和共享資源,例如共享內存區域或管道等
進程與文件之間是兩個不同的概念,簡單來說,檔案描述符是一個文件在系統種的唯一標示;進程則是系統對於一個應用規劃的最小單位
Shell 指令的文件描述符
● Linux shell 可以開啟的文件描述符數量是有限制的,然而 Shell 會保留文件描述符 0
、1
、2
用於指向不同文件,如下表
這裡 Shell 的文件描述符更像是一種指標,它會指向不同文件描述符
shell 文件描述符 | 縮寫 | 說明 |
---|---|---|
0 | STDIN | 標準輸入 |
1 | STDOUT | 標準輸出 |
2 | STDERR | 標準錯誤 |
● 文件描述符數量有限智性
我們可以透過以下指令 查詢 & 設置文件描述符數量
# 查詢 ulimit -n [設置數量]
Shell 指令輸入、輸出、錯誤重定向
A. STDIN
標準輸入:一般來說終端機的 Input 就是鍵盤事件;而 Linux 可以透過重定向符 <
來改變輸入的流向
● 鍵盤作為 Input
● 文件作為 Input(以下把文件內容輸入到 cat 指令中)
cat < testFile
B. STDOUT
標準輸出:在沒有錯誤的情況下,文件的輸出一般來說會指向螢幕;而 Linux 可以透過重定向符 >
(覆蓋) & >>
(追加)來達到輸出重定向
● 使用輸出覆蓋重定向 >
符號
ls -laF > myList.txt
cat myList.txt
# 覆蓋檔案內容
pwd > myList.txt
cat myList.txt
● 使用輸出追加重定向 >>
符號
cat myList.txt
# 追加檔案內容
whoami >> myList.txt
cat myList.txt
C. STDERR
標準錯誤:在 Shell 處理指令期間出錯時,錯誤訊息就會透過 STDERR
指向傳出,預設是指向螢幕輸出;Linux 需要重定向就必須指定檔案描述符 檔案描述符>
● 重新指定檔案描述符 2>
(2
是因為 Shell 保存的STDERR 就是 2
)
# 未重定向
ls -laF testFile myBook
# 重定向
ls -laF testFile myBook 2> stdErr
cat stdErr
● 我們可以多個重複定向
以下將標準錯誤輸出 2
導入到 stdErr 文件中,標準輸出 1
導入到 stdOut 文件中
# 重定向標準輸出、錯誤
ls -laF testFile myBook 2> stdErr 1> stdOut
cat stdOut
cat stdErr
● 特殊重定向
&>
透過
&>
符號可以將輸出、錯誤重定向到同一文件,並且該符號 它更注重於錯誤輸出,所以會將錯誤先輸出,再輸出一般數據ls -laF testFile myBook &> stdOutAll
就是不按照一般順序,而是錯誤全部輸出完,才換到正常的內容
Shell 腳本中重定向
我們可以重新定義腳本的輸出訊息(也同樣分為輸入、輸出、錯誤訊息)
Shell 腳本中使用 FD:臨時重定向
● 在腳本內也可以使用 FD 重定向,其格式如下
輸出命令 >&FD
● 範例:輸出內容到錯誤 FD
#!/bin/bash
echo "Simulate the error" >&2
echo "Normal output" >&1
但由於 &2 也指向 &1 如果使用者不做任何處理看起來像是標準輸出
● 使用命令時,修改 FD 輸出就可以改變指定 FD 到指定檔案中
./shell_redirect.sh 2> shell_err
Shell 腳本中使用 FD:永久重定向(輸入輸出)
● 暫時重定向只能指定某個指令的輸出,無法一次全部指定該腳本的輸入、輸出導向(假設我需要指定全部指令的錯誤輸出到某個檔案中,那就要另外處理)
● 使用 exec
指令:
該命令是用來 啟動一個新的子進程(sub shell)接管當前進程的 FD,來達到腳本的永久定向;其格式如下
exec FD>檔案
使用
exec
重定位後,就不可以再修改,除非你在重定位前有把 FD 存(暫存)起來
A. 使用 exec
範例:重定向輸出(普通輸出、錯誤輸出)
#!/bin/bash
PID=$(echo $$)
exec 2>errorMsg
# 仍可輸出在螢幕上
echo "Redirect the error to 'errorMsg'"
exec 1>normalMsg
# 已重新導向,不會輸出到螢幕上
echo "Redirect the normal to 'normalMsg'"
# 輸出到 normalMsg 中
echo "Normal msg ...~ $(echo $$)" >&1
# 輸出到 errorMsg 中
echo "Error msg !!!~ $(echo $$)" >&2
# 讀取不存在檔案,輸出錯誤到 errorMsg 中
cat Not_exist_file
B. 使用 exec
範例:重定向輸入
#!/bin/bash
# 0 就是標準輸入,將「errorMsg 檔案」作為輸入
exec 0< errorMsg
count=1
while read line ; do
echo "Line=$count: $line"
(( count++ ))
done
C. 使用 exec
範例:除了標準的 FD 以外,我們還可以重定向其他 FD 符
#!/bin/bash
exec 5>fd_5
# 重定向輸出 FD 5
echo "Hello file describe of 5." >&5
echo "Normal output."
切換 FD 重定向
● 前面有提到永久重定向是不可改變的,這個前提是我們沒有把當前 Shell FD 的位置保存,就使用 exec
重向,導致遺失 FD;
知道這個原因後,就可以解決,在永久重定向之前暫存 FD 位置,
A. 輸出 FD 暫存/切換,範例如下
#!/bin/bash
echo "Before redirct the 1."
# 3 導向到 1
exec 3>&1
echo "Hello I'm 3." >&3
# 1 導向到 fd_1(無法正常輸出)
exec 1>fd_1
echo "After redirect the 1."
# 1 重新導向到 3(而 3 又導向 1,也就能正常輸出)
exec 1>&3
echo "Restore the 1"
● 概念圖如下
B. 輸入 FD 暫存/切換,範例如下
#!/bin/bash
# 將 0 的位置暫存到 9
exec 9<&0
# 將 testFile 作為輸入讀取
exec 0< testFile
while read line ; do
echo "read: $line"
done
# 將輸入還給 0 fd(也就可以正常接收使用者鍵輸入)
exec 0<&9
read -p "Finish read file, do you want quit?" ans
case $ans in
Y|y) echo "Bye" ;;
N|n) echo "To do something" ;;
*) echo "???"
esac
Here 文件:<<
符號
● Here 文件:
把「標準輸入」重新定向到另一個指令(也就是把輸入直接賦予到令一個指令),這可以用來輸入多行指令;
使用 <<
符號就可以控制 Here 文件,格式如下
#!/bin/sh
DATE=$(date)
cat << EOF
Date: $DATE
The output above is from the Unix date command.
EOF
● 以上使用
EOF
作為開頭、結尾用語(也可以使用其他字串,但是一定要成對出現)
讀寫同 FD:<>
● 雖然很少這種情況(讀寫同 FD),但還是可以這樣操作;在同 FD 讀、寫時,Linux 會使用指針操作寫入的內容
範例如下
#!/bin/bash
# 重新 FD 3 的輸入、輸出到 testFile
exec 3<> testFile
read line <&3
echo "FD 3 read the 'testFile': $line"
echo "Write something to FD 3" >&3
● 這裡發生一種狀況,是原來的文件被覆蓋了,但可以發現 覆蓋的內容是依照指針移動的位置開始覆蓋
目前是因為讀取了
Hello
所以寫入時從Hello
之後開始寫入
手動關閉 FD:&-
符號
● 一般來說,文件描述符的操作在 Shell 腳本結束之後就會自動關閉,如我們要想要提前關閉,可以使用以下指令格式關閉 FD
# 使用 ‵>&-‵
exec FD_NUM>&-
手動關閉 FD 範例:
#!/bin/bash
exec 3> closeFdFile
echo "Hello shell script." >&3
exec 3>&-
echo "After close the fd 3." >&3
可以發現,關閉前的數據確實被輸出到檔案中,但關閉 FD 之後,仍要操作 FD 就會拋出錯誤
● 可以再次開啟同樣的 FD ?
可以,不過如果仍指定輸出相同文件,那舊有數據就會被覆蓋
#!/bin/bash exec 3> closeFdFile2 # 這行數據會被覆蓋 echo "Hello shell script." >&3 exec 3>&- # 開啟同個文件 exec 3> closeFdFile2 echo "After close, and open the fd 3." >&3
查看 FD 指向
有時我們會需要查看某個文件的 FD (文件描述符) 被定義到哪個位置上,那我們就可以 使用 lsof
命令 (list open files
);其輸出的欄位意義如下表
輸出列表 Item | 說明 | 補充 |
---|---|---|
COMMAND | 正在運行的命令 | - |
PID | 進程 PID | - |
USER | 文件所屬哪個進程 | - |
FD | 當文件的檔案描述符 FD_<符號> | 其中符號 u 代表可讀寫、w 代表可寫、r 代表可讀 |
TYPE | 文件類型 | CAHR 字符、BLK 快設備、DIR 資料夾、REG 常規文件 |
DEVICE | 主/從設備號 | - |
SIZE/OFF | 文件大小 | 可有可無 |
NODE | 文件節點號 | - |
NAME | 文件名 | - |
# -a 代表之後的條件使用 AND 運算
# -p 指定進程
# -d 指定 FD
lsof -a -p $$ -d 0,1,2
腳本中使用 lsof 命令
● 以下使用在腳本中呼叫 lsof
命令,並查看相對應的 FD
#!/bin/bash
exec 3> testFile3
exec 6> testFile4
exec 7< testFile
lsof -a -p $$ -d 0,1,2,3,6,7
tmp 目錄
在根目錄下有一個 /tmp
臨時目錄,該目錄是用來存放暫時文件的,在該目錄下的文件,會在系統啟動時自動清除
創建暫存文件、目錄 - mktemp
● 透過 mktemp
命令,我們可以簡單地在當前目錄下創建文件,並且該文件還有以下特色
● 在當前目錄下 不重名
● 不受 umask
設定影響,直接將文件設定為創建者所有
一般文件會受到
umask
設定影響
並非所有 Unix 系統都有
mktemp
指令,可以透過 apt 命令安裝coreutils
包
● 在本地使用 mktemp
指令創建只需要指定一個 文件名模板 即可;文件名模板 可以包含任意文件名,之後 末尾有 6 個 X
符號;
格式如下
# 文件名模板
任意文件名.XXXXXX
A. 範例:創建隨機名檔案,並返回檔案名
mktemp myFile.XXXXXX
ls -laF
B. 範例:創建暫存目錄只需要加上 -d
選項即可,創建臨時目錄完畢後會 返回檔案名
mktemp -d myDir.XXXXXX
ls -laF
創建暫存文件:/tmp
目錄
● 同樣使用 mktemp
命令,不過這次 使用 -t
選項,透過這個選項就可以在 /tmp
目錄下創建暫存文件
A. 範例:創建暫存文件在 /tmp
目錄
mktemp -t myTmpFile.XXXXXX
B. 範例:使用在腳本中 mktemp
指令創建一個暫存文件,並對該文件輸入
#!/bin/bash
MY_TEMP_FILE=$(mktemp -t MyTempShell.XXXXXX)
echo "Hello world~" > $MY_TEMP_FILE
echo "Yo yo temp file~" >> $MY_TEMP_FILE
if [ -e $MY_TEMP_FILE ] ; then
echo "Show $MY_TEMP_FILE"
cat $MY_TEMP_FILE
rm -f $MY_TEMP_FILE
else
echo "$MY_TEMP_FILE not exists"
fi
其他
導向空文件:/dev/null
● 如果想忽略腳本中某些部分的錯誤,可以將 FD 2(STDERR
) 重新導向到 /dev/null
文件上;
輸出到
/dev/null
的內容不會保存
● 範例:將指令錯誤導向 /dev/null
ls -laF NotExistFile testFile 2> /dev/null
同時紀錄 & 輸出:tee
命令
● 透過 tee
命令可以簡單的把內容 同時輸出到螢幕上、檔案中,其格式如下
tee 輸出檔案名
A. 範例:輸出結果至螢幕,並寫入到檔案
date | tee testTee
cat testTee
B. 範例:使用 -a
追加數據到指定檔案中
默認情況下
tee
會覆蓋檔案中的內容,如果要追加檔案內容需使用-a
選項
who | tee -a testTee
cat testTee
更多 Linux Shell 知識
Linux 環境、服務:用戶、權限管理
● Linux 環境、服務、管理:
在這個主題中,你可以探索各種 Linux 環境、服務和管理相關的重要概念…
● 了解 Linux 組成:內核責任、GNU 工具、桌面環境 | 基礎版到發行版
在這裡,你將了解 Linux 系統的基本組成部分,包括內核、GNU 工具和桌面環境,並了解不同 Linux 發行版之間的差異
● 探索 X Window 服務與桌面環境:Linux 視窗 XWindow 服務、D-Bus 機制
這裡探討了 Linux 系統中 X Window 服務和 D-Bus 機制的工作原理,讓你深入了解桌面環境的背後運作方式
● Linux 系統管理入門:安全性、用戶管理與權限設定指南
在這個指南中,你將學習如何管理 Linux 系統,包括安全性、用戶管理和權限設定,以確保系統的安全和有效運作
● 認識身份驗證與權限管理:UNIX UID、使用者驗證與 PAM | PAM 設定
這裡介紹了 UNIX 系統中的身份驗證機制和權限管理方式,包括 UNIX UID、使用者驗證和 PAM(
Pluggable Authentication Modules
)設定
Linux Shell 相關知識
● Linux Shell 相關知識:
這個主題涵蓋了各種與 Linux Shell 相關的知識,從基礎到進階都有…
● Shell 基礎知識:包括 Shell 的基礎使用以及差異,還有變數以及計算
● 認識命令行:Shell 類型、命令差異 | Sub Shell 關係 | Builtin 命令
這裡介紹了命令行中不同類型的 Shell,以及各種 Shell 命令之間的區別和內建命令的用法
● Shell 全局及區域變數、特殊變數及環境變數 | Shell 啟動順序 | Array 變數
學習如何在 Shell 中使用全局變數、特殊變數和環境變數,以及如何設置和管理這些變數
● 探索腳本與命令:Shell 腳本的必備相關知識 | 腳本的數學運算
這裡介紹了 Shell 腳本的基本知識和常用技巧,包括如何執行腳本和進行數學運算
● Shell 結構化
● Shell 腳本程式中的條件語句和高級特性 | Shell 結構化 & 判斷
該篇文章是介紹結構化腳本的基礎以及特殊的高級技巧,轉著於腳本中的邏輯判斷
● 掌握 Bash 腳本中的迴圈與循環控制技巧 | Shell 結構化 & 循環
接著是腳本中的循環與控制的技巧,它可以讓我們在 Shell 腳本中有更多的判斷與技巧
● 探索 Shell 函數與腳本庫 | Shell 結構化 & 函數
這裡你將會學在到結構化腳本的重要技巧「函數」,它可以讓你建立可重複利用的腳本,以節省我們之後開發的時間
● Shell 掌握參數處理與用戶輸入技巧 | Shell 結構化 &輸入互動
不可互動的腳本有時候相對無趣,這篇文章有分享如何讓腳本與使用者產生交互互動,並依照使用者輸入的參數做讀取
● Shell script 進階
● Shell 命令輸入與輸出指南:掌握標準文件描述符與重定向 | 臨時文件
掌控 Shell、指令的輸出輸入是成為進階使用 CLI 之人必經的一課,透過這篇文章可以了解到輸出輸入、臨時文件等等資訊
● 探索 Linux 訊號與後台進程管理:安排定期啟動腳本 | 運行時啟動腳本
信號可以算是 Shell 腳本與 Linux 系統之間的通訊方式,透過這個文章,我們可以了解到信號與 Shell 腳本的安排時間
Linux 硬體規劃:檔案系統、分區
● Linux 硬體規劃:檔案系統、分區:
最後,這個主題涵蓋了 Linux 系統中硬體結構相關的重要概念…
● Shell 文件和目錄操作的常用命令和技巧 | 尋找檔案
這裡介紹了 Shell 中常用的文件和目錄操作命令,包括如何尋找文件和管理文件系統
● 深入探索 Linux 文件系統與硬體管理 | 分區、檔案系統、邏輯卷 | inode
學習如何管理 Linux 文件系統,包括分區、文件系統和邏輯卷等相關概念,以及文件系統中的 inode 機制
● 理解 Unix/Linux 設備偵測、建立 | 認識與使用 udevd | SCSI 與 Linux 核心
這裡介紹了 Unix/Linux 系統中的設備偵測和管理,包括如何使用 udevd 和 SCSI 相關的 Linux 核心知識