Photo by Sai Kiran Anagani on Unsplash

Linux命令個人筆記 (更新到find)

LUFOR129

--

linux命令太多太雜參數太難記了,每次要用時就要google一次。不如把過去、現在、未來做的筆記整理到一處,方便未來觀看。

先更新linux三劍客: grep、sed、awk,未來再慢慢把touch、find、xargs、cut…等命令與參數更新上去 (希望不要像OpenCV那篇一樣太久沒用就斷更了XD),如果時間允許,未來會再寫一個shell筆記。

以下會針對每一個工具做講解,另外linux 文本pipeline可以用| 做串聯,同時執行用;如果命令太長了可以用\區隔。

一、 grep

說到Linux文件處理,不得不提到三大工具: grep、sed、awk

  1. grep : 強大的文本搜尋工具,使用正則來匹配文本。
  2. sed : 簡單對每一列文本做編輯,並向下輸出 (stream editor)
  3. awk : 用於比sed更複雜的文本編輯。

grep 是強大文本搜尋工具,將正規表達式匹配文本的列(row)輸出出來,關於正規表達,我在爬蟲(三)有寫過,基本用法如下:

grep [option] <regex> <file>在多个文件中查找: 
grep <regex> file_1 file_2 file_3 ...
grep <regex> "*.txt"
[option]
-v 输出regex匹配之外的所有列 (倒轉)
-n 输出包含匹配字符串的行数
-l 搜索多个文件并查找匹配文本在哪些文件中
-i 忽略大小寫
-r 當前路徑下所以子路徑查找(遞迴)
-A <數字> 是after輸出找到後下面幾列
-B <數字> 是before輸出匹配後上面幾列
-e -E 多條件,如:
grep -e <regex1> -e <regex2> <file>
grep -E <regex1|regex2> <file>

grep在跟find (找尋檔案) 同時用有奇效

情境1 : 想要知道我的資料夾內所有py檔有哪幾列用到time?

# 有那幾列(row)用到time
grep "time" -n *.py
# 有哪些檔案用到time
grep "time" -l *.py

情境2 : 尋找在目錄下所有三層內py檔中含有try的檔案?

# find就是尋找檔案,基本格式如下(廣度優先)
find <自哪開始> -name <file>
# find 找到三層內所有py檔案
find . -maxdepth 3 -name *.py
# find輸出會自動加上\n。
# 所以加上-print0代表輸出用-0 (NULL '')字符替代而不是\n。
# 然後告訴xargs 用-0 (NULL) 來做切割檔案路徑之間
# xargs是標準輸入這些文件檔案並傳給下一個動作 (grep)
find . -maxdepth 3 -name *.py -print0|xargs -0 grep -l "try"
輸出結果

二、 sed

sed 用於自動編輯文檔,一次處理一列內容,常用於搜尋與取代,可以匹配支援正則。會先把要處理的列放在緩衝區中,再做處理。完成後將緩衝區送往螢幕,接著下一行。原始文檔內容不會被改變。基本用法如下:

sed [option] '範圍 操作' <file>e.g.:  
sed -n -e "1a Hello world" -e "1,5p" log.txt
在第一列後面加上hello,並輸出1~5列
[option]
-i 寫入進原始檔案 (小心)
-n 只輸出匹配到的結果
-e 匹配多個項目時
[範圍]
不給範圍: 默認是全進行匹配
/regex/ 被regex匹配到的
n,m 第n列到第m列 (從1開始數包含 e.q. 1,3 第1~第3列含)
n 第n列
n,$ 第n列到結尾
n,+3 第n列到其後3列
/regex1/,/regex2/ 被regex1匹配到的列到regex2匹配到的列
[操作]
a <sent> 新增,後面接的字串在下一列出現
d 刪除匹配列
p 打印
w <file> 把匹配內容寫到其他地方
e.g. 想要查詢route中docker的部分,並寫入output.txt
route| sed -n "/docker/ w output.txt"
e.g. 刪掉目錄內所有py檔內所有註解#並輸出
find ./ -name "*.py" | xargs sed "/^[ ]*#/d"

實際上sed最強大的是他的替換功能,可以根據正則做匹配來替換,使用如下:

sed [option] '範圍 s/匹配/替換/操作' <file>其中替換的&代表匹配到的原始數據[操作]
g 默認是只匹配一次,使用g後是全文匹配
p 輸出內容,搭配-n服用
w 寫入
i 忽略大小寫
\b 代表full match
比如\blucky\b只匹配lucky
匹配時特殊符號要加上跳脫\,含{}()
不過其實你可以用|^@!來代替sed跳脫
e.g. 抓出所有成功連進我的nginx的baidu IP,並在IP前面加上$字
grep -in "baidu" /var/log/nginx/access.log | sed "s/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/$&/g" > ~/output.txt
其實匹配的是 ([0-9]{1,3}\.){3}[0-9]{1,3},但是{}()都要跳脫
結果

三、 awk

其實awk幾乎可以算是一種程式語言了,他可以宣告變數、有條件判斷、有for迴圈,擁有極為強大的文本處理能力。

awk可以就是將文件逐列(row)讀入,以空白為默認每行(column)切片,切開後再做處理。和sed比較是,sed核心是正則、awk核心是格式化。第幾個column用$數字來表示($1開始數,$0是整列)看下圖:

基本用法如下,多命令用;來pipeline執行:

# awk 運作模式是 "預處理+逐行處理+最終處理"awk [option] 'BEGIN{命令} pattern{命令;命令} END{命令}' <file>e.g. 取出自己的ip位置
ifconfig enp2s0|grep "inet addr"|awk -F : '{print $2}'|awk '{print $1}'
先取出有inet addr那一row,再依照:切開,再依照空白(默認)切開
[option]
-F 指定切開的依據(默認為空白)支援正則
e.g. 你可以 awk -F ':|,|$' (由:$,切開) -F " |:" (由空白與:切開)
-v 附值
e.g. echo "world" |awk -v name="hello" '{print name" "$0}'
得到hello world
[內建變數]
$0是整列,$1切開後第一個段落,$2第二個段落...
NR 目前第幾row (因為是由上而下一列一列切割,由1開始數)
NF 當前row被切割後有幾column,NF是最後一column,NF-1倒數第二column
FILENAME 文件名稱
[內建輸出]
print 標準輸出
printf format輸出
e.g. awk '{printf “%s %d %d %d\n”,$1,$2,$3,$4}'
在輸出中可以做一些簡單的運算
e.g. 所有用戶id+1
awk -F : -v i=1 '{print $3+i}' /etc/passwd
BEGIN 定義輸出頭
END 定義輸出尾
e.g.印出文件所有列的第1,7個,在所有列前加入user,後加入hello world
awk -F : 'BEGIN{print "user"} {print $1","$7} END{print "hello world"}' /etc/passwd
[pattern]
就是正則表達式,可以匹配對應的列(用//包覆)
e.g. root開頭所有列,並輸出第1,7個
awk -F : '/^root/{print $1","$7}' /etc/passwd
/etc/passwd

awk中可以實現一些簡單的程式邏輯,現在來針對ls -l 做操作吧 :

awk 語法跟C差不多(個人覺得)[變數操作]
我們來累加日期吧 (就是十二月 27的那個27)
ls -l|awk -F " " -v sum=0 '{sum+=$7} END{print "total sum:",sum}'
你也可以把變數寫在BEGIN,如下:
ls -l |awk 'BEGIN{sum=0} {sum+=$7} END{print "total sum: ",sum}'
另外變數也支援++[條件判斷]
也就是if、else、if else和命令式一起包覆在{}中,
如過是2020年的資料夾印出整串,else只印出資料夾名稱
ls -l|awk '{if($8==2020){print $0}else{print $9}}'
三元運算式
ls -l|awk '{print $8==2020?$0:$9}'
計算各年份檔案個數
ls -l|awk 'BEGIN{nine=0;twen=0} {$8==2020?twen++:nine++} END{print "2019: ",nine,"2020: ",twen}'
[for迴圈與數組(dict)]
循環支援for、while、do、break、continue
嚴格來說,awk只有dict沒有array,不過這不妨礙我們使用數字當作是key值模仿array
給予每個文件夾一個遍號
ls -l| awk 'BEGIN{count=0} {name[count] = $9;count++} END{for(i in name){print i," ",name[i]}}'
[執行命令與重要函數]
system(cmd),輸入進去system後即可
例如ping 所有ifconfig的IP
ifconfig | grep "inet addr"|awk '{print $2}'|sed -n 's/addr://p'|awk 'BEGIN{cmd="ping "} {system(cmd $0)}'
先grep取出inet addr的列,切割只取第二行,刪掉addr: (得到純IP),然後ping他

四、find

find 是unix中查找文件位置用的,他會自動遞迴資料夾內的所有檔案來找到你所需要的檔案名稱。輸出時會自動在檔案串之間加入\n來方便使用者觀看。

基本用法:
find <自哪開始> -<file>
e.g.
find . -name 1.txt
find . -name *.txt
[參數]
1. 根據文件大小
-size <大小>
2. 根據文件類型
-type <文件類型> ex:文件類型(f是文件、d是dir)
3. 根據修改時間 (c指change,含修改文件內容、修改文件權限)
-ctime <天數>
<天數>: -1 一天之內,1 剛好一天,+1 一天前
-cmin <分鐘>
-mtime <天數> (modify,指修改文件內容,不含修改文件權限)
-atime <天數> (access,多久讀取)
4. 遞迴文件深度 (重要)
-maxdepth <層數>
find . -maxdepth 3 -ctime 1 -name "*.txt"
# 找出一天內、距離目前資料夾3層內的txt檔
### 結合xargs ###
find . -maxdepth 2 -name "*.txt" -print0 | xargs -0 ls -l
# 默認是由\n切割,所以我們用-print0代表改用NULL(-0)替代\n,然後傳遞給xargs說用-0切割,接者用ls -l 來查看每個檔的詳細資訊。
結果

五、 cut

// towrite

參考資料

  1. https://zhuanlan.zhihu.com/p/68188159

2. https://www.bilibili.com/video/av68266603

3. https://zhuanlan.zhihu.com/p/108508116

--

--

LUFOR129
LUFOR129

No responses yet