別催了,別催了,這篇文章我一次性把Shell的內容說完
Shell 搜尋與匹配
1、在檔案中查詢字串
grep 命令可以搜尋檔案,查詢指定的字串。
$ grep myvar *.c
在這個例子中,我們搜尋的檔案全都位於當前目錄下。因此,我們只使用了簡單的 shell 模式 *.c 來匹配以 .c 結束的檔案,並沒有在檔名前再新增路徑。
但並非所有待搜尋的檔案都老老實實地待在當前目錄下。但因為shell 並不在意你輸入多少路徑名,所以我們也可以這麼寫:
$ grep myvar ../lib/*.c ../server/*.c ../cmd/*.c */*.c
如果待搜尋的檔案不止一個,grep 會在輸出前加上檔名以及冒號,然後是該檔案中包含 grep 搜尋內容的文字。
grep 的第一個(非選項)引數可以是一個簡單的字串,也可以是更復雜的正則表示式(regexp)。正則表示式不同於 shell 的模式匹配,儘管兩者有時看起來差不多。
常見錯誤
忘記指定 grep 的輸入,例如 grep myvar。這種情況下,grep 會認為你要從 STDIN 提供輸入,而你以為它會讀取檔案,於是 grep 就乾等著,無所事事。
2、只顯示包含搜尋結果的檔名
你需要找出包含特定字串的檔案,但是不想看到其所在的文字行,只用輸出檔名即可,經常在線上為了搜尋配置檔案。
用 grep 的 -l 選項僅顯示檔名即可,如下:
$ grep -l myvar *.c
both.c
good.c
somio.c
$
如果在一個檔案中找到了多次匹配,grep 仍然只輸出該檔名一次。如果沒有找到匹配,則什麼都不輸出。
由於這些檔案包含了你要查詢的字串,如果想據此構建一個待處理
檔案的列表,選項 -l 就能派上用場了。將 grep 命令放進 $(),然後就可以在命令列上使用這些檔名了,如下:
rm -i $(grep -l 'This file is obsolete' * )
刪除包含字串“This file is obsolete”的檔案,我們給 rm 加上了 -i 選項,以便在刪除每個檔案前都先詢問你。
3、不區分大小寫搜尋
你想要在日誌檔案中不區分大小寫地搜尋字串(如“error”),以匹配該字串的所有出現。用 grep 的 -i 選項忽略大小寫,如下所示:
grep -i error logfile.log
不區分大小寫的搜尋能夠找出包含“ERROR”、“error”、“Error”的日誌訊息,“ErrOR”和“eRrOr”這樣的也不例外。該選項在查詢大小寫混合的單詞時尤其管用,或者對於查詢的內容無法確定大小時。
4、縮減搜尋結果
如果搜尋返回的結果不符合預期,其中包括許多並不需要的內容。將結果通過管道傳給 grep -v 並用表示式描述出你不想看到的內容。假設你想在日誌檔案中找出整個 12 月的日誌訊息。你知道日誌檔案用字母縮寫 Dec 代表 12 月,但不敢肯定總是如此,為了確保找出所有的日誌訊息,輸入下列命令:
grep -i dec logfile
得到的結果卻如下所示:
...
error on Jan 01: not a decimal number
error on Feb 13: base converted to Decimal
warning on Mar 22: using only decimal numbers
error on Dec 16 : the actual message you wanted
error on Jan 01: not a decimal number
...
一種快而糙的解決方案是,將第一次得到的結果通過管道傳給另一個grep,由後者過濾掉所有的“decimal”。
grep -i dec logfile | grep -vi decimal
將多個 grep 串聯在一起(因為前所未見、出乎意料的匹配會不斷出現),逐步過濾搜尋結果,直至滿意,這種做法並不鮮見。
-v 選項非常方便,你只需要記住該排除什麼就行了。
5、搜尋更復雜的模式
grep 中的正則表示式提供了更為強大的模式匹配功能,能夠滿足大部分需求。正則表示式描述了待匹配字串的模式。字母字元(或者對於 shell沒有特殊含義的其他字元)只匹配自身。“A”匹配 A,“B”匹配B,這沒什麼好說的。另一個重要的規則是按位置組合字母,如 AB匹配“AB”。這看起來也是顯而易見的。但是,正則表示式還定義了其他一些特殊字元,它們既可以單獨使用,也可以與其他字元結合,從而形成更為複雜的模式。
第一個特殊字元是點號(.),它可以匹配任意單個字元。因此,.... 可以匹配任意 4 個字元;A. 匹配“A”以及緊隨其後的任意單個字元;.A. 匹配任意單個字元,然後是“A”,接著是任意單個字元(未必和匹配到的第一個字元相同)。
第二個特殊字元時星號(**),匹配上一個字元的 0 次或多次出現,因此,A* 匹配 0 個或多個“A”字元,.* 匹配 0 個或多個任意字元(如“abcdefg”、“aaaabc”、“sdfgf ;lkjhj”,甚至是空行)。
那麼 ..* 是什麼意思?它匹配任意單個字元以及緊隨其後的 0 個或多個任意字元(也就是一個或多個字元,但不能是空行)。
如下所示,我們知道一行的某些單詞,我們想模糊匹配,如下操作:
grep -E "1.*22" 2.text // 匹配1開頭,任意個字元後是22
結果如下:
1898090808098822:
Shell 檔案查詢
1、查詢所有的txt檔案
檔案系統中到處都是 txt 檔案。你想將它們集中到一個位置。那麼我們該如何做呢?
find 命令可以找出符合要求的所有檔案並執行命令,將其移動到指定位置。例如:
find . -name '*.txt' -print -exec mv '{}' /txts \;
find 命令的語法和其他 Unix 命令不同,其選項並不是那種典型的連字元加上單字母,後面再跟上若干引數。find 命令的選項看起來像是簡短的單詞 1,依照邏輯順序出現,並描述要查詢哪些檔案以及如何處理找到的檔案(如果存在的話)。這種像單詞一樣的選項通常稱為謂詞(predicate)。
find 命令的第一個引數是待搜尋的目錄。典型用法是用點號(.)代表當前目錄,不過你也可以提供一個目錄列表,甚至通過指定根目錄(/)來搜尋整個檔案系統(只要許可權允許)。
示例中的第一個選項(謂詞 -name)指定了要搜尋的檔案模式。其語法和 bash 的模式匹配語法差不多,因此 *.txt 能夠匹配所有以“.txt”結尾的檔名。匹配該模式的檔案被認為返回的是真(true),接著將其交給下一個謂詞進行處理。
find 會遍歷檔案系統,將找到的檔名交給謂詞測試。如果謂詞返回真,就通過。如果返回假,則不再繼續往下進行,會接著處理下一個檔名。
謂詞 -print 很簡單。它總是返回真,同時會將檔名列印到標準輸出,因此,能在謂詞序列中通過測試而到達這一步的檔案都會輸出其名稱。如果不寫,預設會帶有這個謂詞。
-exec 就有點怪異了。到達這一步的檔名都會變成接下來要執行的命令的一部分。剩下一直到 ; 的這部分就是命令,其中的 {} 會被替換成已查詢到的檔名。因此,在上面的例子中,如果 find 在./txt/jazz 子目錄中找到名為 1.txt 的檔案,那麼要執行的命令就會是:
mv ./txt/jazz/1.txt /txts
所有匹配指定模式的檔案都會執行命令。如果找到的檔案數量眾多,那麼命令的執行次數自然也不會少。
2、提升已找到檔案的處理速度
按照上面的例子,find命令會為每個名字符合要求的檔案執行命令,但是當檔案過多時命令自然會很慢,那麼我們如何提高速度呢?
xargs 命令從標準輸入中接收以空白字元分隔(指定 -0 時除外)的檔名,然後對儘可能多的檔案(略微少於系統的 ARG_MAX 值,參見 15.13 節)執行指定命令。由於呼叫其他命令會帶來不小的開銷,因此使用 xargs 可以顯著提升操作速度,因為它能夠儘量減少命令的呼叫次數,而不是每個檔案都呼叫。如下所示:
find . -name '*.txt' -print | xargs mv '{}' /txts;
3、查詢檔案時不區分大小寫
有些 TXT 檔案的副檔名是 .TXT,而不是 .txt。查詢時該如何兼顧兩者?
用 -iname 謂詞(如果使用的 find 版本支援)執行不區分大小寫的搜尋。例如:
find . -iname '*.txt' -print | xargs mv '{}' /txts;
4、按日期查詢檔案
幾個月前,有人給你發了一張 JPEG 圖片,你接收後就儲存了起來,但現在記不清放哪了。怎樣才能找到這張圖片呢?
使用 find 命令的 -mtime 謂詞來檢查檔案的最後修改日期。例如:
find . -name '*.jpg' -mtime +90 -print
-mtime 謂詞接受一個引數,用於指定要搜尋的時間段。90 代表 90天。在數字前使用加號(+90)表明要搜尋的檔案是在 90 天前修改的。使用減號(-90)表明檔案是在 90 天以內修改的。如果既沒減號,也沒加號,則表明正好就是 90 天。
find 還可以使用邏輯運算子 AND、OR、NOT,如果知道檔案修改時間至少在一週(7 天)前,但不超過 14 天,那麼就可以像下面這樣將兩個謂詞結合起來。如下所示:
find . -mtime +7 -a -mtime -14 -print
5、按型別查詢檔案
你正在查詢名稱中帶有單詞“java”的目錄。先嚐試了以下命令。
find . -name '*java*' -print
找到的檔案太多了,其中還包括檔案系統中所有的 Java 原始碼檔案。使用 -type 謂詞只選擇目錄。如下:
find . -type d -name '*java*' -print
同樣,我們可以使用 -type f指定指查詢檔案。
我們將 -type d 放在前面,然後是 -name 'java'。兩者的順序並不影響最終結果,但將 -type d 放在謂詞列表的最前面能略微提高搜尋效率:對於碰到的每個檔案,先測試其是否為目錄,如果是,才測試名稱是否符合模式。目錄的數量比檔案要少一些。因此,這種測試順序使得大部分檔案不用再進一步比較名稱了。
6、按內容查詢檔案
你之前寫了一份重要的信件,並將其儲存為以 .txt 為副檔名的文字檔案,但現在想不起檔名的其餘部分了。除此之外,唯一記得的就是信件內容中用到過單詞“portend”。那麼該如何查詢已知部分內容的檔案呢?
如果檔案就在當前目錄下,可以使用簡單的 grep 命令。
grep -i portend *.txt
如果還沒找到,我們換用一個更完備的解決方案:find 命令。使用其 -exec 選項對滿足謂詞的檔案執行命令。你可以按下列方式使用grep 或其他實用工具:
find . -name '*.txt' -exec grep -Hi portend '{}' \;
或者也可以使用xargs
find . -name '*.txt' | xargs grep -Hi portend
Shell 文字解析awk
1、保留部分輸出
你需要用某種方法保留部分輸出,丟棄其餘輸出。比如我們日常線上日誌,我們可能會輸出很多屬性,但是真正能用來解決實際問題的,大多是我們輸出的文字資訊。以下程式碼會打印出所有輸入行的第一個欄位:
awk '{print $1}' myinput.file
欄位之間以空白字元分隔。實用工具 awk 從命令列上指定的檔案中讀取資料,如果沒有指定檔案,則從標準輸入讀取。$1代表每行以空格分割後的第一列。
除了上面的寫法,我們還可以通過管道傳入:
cat myinput.file | awk '{print $1}'
awk 的用法多變。最簡單的用法就是從輸入中打印出所選的一個或多個欄位。欄位之間以空白字元分隔(也可以用 -F 選項指定分隔字元),編號從 1 開始。欄位 $0 代表整個輸入行。
2、保留部分輸入行
你只想保留部分輸入行,例如第一個和最後一個欄位。舉例來說,你希望 ls 只列出檔名和許可權,不需要 ls -l 所提供的其他資訊。可惜的是,ls 並沒有相應的選項能夠按照這種方式限制輸出。可以通過管道將 ls 的輸出傳給 awk,並從中挑選出你需要的欄位,如下:
$ ls -l | awk '{print $1, $NF}'
total 151130
-rw-r--r-- add.1
drwxr-xr-x art
drwxr-xr-x bin
-rw-r--r-- BuddyIcon.png
drwxr-xr-x CDs
drwxr-xr-x downloads
drwxr-sr-x eclipse
...
$
如果我們用ls -l 命令的輸出。其形式如下所示:
drwxr-xr-x 2 username group 176 2026-10-28 20:09 bin
對於 awk 而言,解析這種輸出易如反掌(在 awk 中,預設的欄位分隔符為空白字元)。
在輸出檔名時,我們用了點小技巧。在 awk 中,各種欄位是用美元符號和欄位編號來引用的(如 $1、$2、$3),而且 awk 還有一個內建變數 NF,其中儲存著當前行中的欄位總數,$NF 總是引用最後一個欄位。(例如,ls 的輸出行共有 8 個欄位,因此變數 NF 的值就是 8,$NF 指向的就是輸入行中的第 8 個欄位,在這個例子中就是檔名)。
注意:讀取 awk 變數時不需要使用 $(這一點和 bash 變數不同)。NF 本身就是一個有效的變數引用。在其之前加上 $ 就將其含義從“當前行的欄位總數”改成了“當前行的最後一個欄位”。
3、顛倒每行的單詞
如果想按照逆序輸出輸入行中的單詞。通過下列指令碼:
$ awk '{
> for (i=NF; i>0; i--) {
> printf "%s ", $i;
> }> printf "\n"
> }' <filename>
字元 > 不用你輸入,shell 會輸出該字元來提醒你還沒有敲完命令(shell 在查詢能配對的單引號)。由於 awk 程式位於單引號中,因此 bash shell 允許我們輸入多行程式碼,同時使用 > 作為輔助提示符,直到我們給出與先前匹配的結束單引號。考慮到可讀性,我們在程式中加入了空白字元,不過也完全可以寫成一行。
$ awk '{for (i=NF; i>0; i--) {printf "%s ", $i;} printf "\n"}' <filename>
awk 語言的 for 迴圈語法和 C 語言中的非常相似。我們用 for 迴圈從最後一個欄位開始倒著處理到第一個欄位,同時輸出每個欄位的內容。
4、彙總數字列表
如果你需要彙總數字列表,其中有些數字並未出現在行中。用 awk 先過濾出待彙總的欄位,然後再做彙總。這裡我們要對 ls -l 命令輸出的檔案大小進行彙總。如下:
ls -l | awk '{sum += $5}; END {print sum}'
我們要彙總 ls -l 輸出的第 5 個欄位。ls -l 的輸出如下所示:
-rw-r--r--. 1 root root 37 12月 23 21:44 2.text
-rwxr--r--. 1 root root 110 12月 10 02:20 ifTest.sh
各個欄位分別為:許可權、連結、所有者、所屬組、大小(以位元組為單位)、最後一次修改日期、最後一次修改時間,以及檔名。我們只對檔案大小感興趣,因此在 awk 程式中用 $5 來引用該欄位。我們在花括號({})裡放置了兩段 awk 程式碼,注意,awk 程式中可以有多個程式碼段(或程式碼塊)。前有關鍵詞 END 的程式碼塊僅在程式其他部分完成後執行一次。
本文由
傳智教育博學谷
教研團隊釋出。如果本文對您有幫助,歡迎
關注
和點贊
;如果您有任何建議也可留言評論
或私信
,您的支援是我堅持創作的動力。轉載請註明出處!
- ElasticSearch還能效能調優,漲見識、漲見識了!!!
- 【必須收藏】別再亂找TiDB 叢集部署教程了,這篇保姆級教程來幫你!!| 博學谷狂野架構師
- 【建議收藏】7000 字的TIDB保姆級簡介,你見過嗎
- Tomcat架構設計剖析 | 博學谷狂野架構師
- 你可能不那麼知道的Tomcat生命週期管理 | 博學谷狂野架構師
- 大哥,這是併發不是並行,Are You Ok?
- 為啥要重學Tomcat?| 博學谷狂野架構師
- 這是一篇純講SQL語句優化的文章!!!| 博學谷狂野架構師
- 捲起來!!!看了這篇文章我才知道MySQL事務&MVCC到底是啥?
- 為什麼99%的程式設計師都做不好SQL優化?
- 如何搞定MySQL鎖(全域性鎖、表級鎖、行級鎖)?這篇文章告訴你答案!太TMD詳細了!!!
- 【建議收藏】超詳細的Canal入門,看這篇就夠了!!!
- 從菜鳥程式設計師到高階架構師,竟然是因為這個字final
- 為什麼95%的Java程式設計師,都是用不好Synchronized?
- 99%的Java程式設計師者,都敗給這一個字!
- 8000 字,就說一個字Volatile
- 98%的程式設計師,都沒有研究過JVM重排序和順序一致性
- 來一波騷操作,Java記憶體模型
- 時隔多年,這次我終於把動態代理的原始碼翻了個地兒朝天
- 再有人問你分散式事務,把這篇文章砸過去給他