cat(concatenate)
讀取、顯示、拼接檔案內容
把來自標準輸入的內容和檔案內容拼接起來:
echo 'from stdin' | cat - test.sh
cat 命令用 - 表示標準輸入
If file is a single dash (`-') or absent, cat reads from the standard input.
其他常用功能選項:
# 給檔案內容添上行號
cat -n test.sh
# 把檔案中的連續多個空行壓縮成一個
cat -s test.sh
find
基本規則
從檔案目錄向下走訪,匹配符合條件的,處理
# 列出目前目錄下所有檔案/資料夾、子檔案/資料夾
find .
# 用 \0 分隔(檔案路徑裡有換行字元時有用)
find . -print0
# 萬用字元
find -name "*.js"
# 忽略大小寫
find -iname "*.js"
# 多條件
find . \( -name "e*" -o -name "s*" \)
# 路徑匹配
find ../tnode -path "*node*"
# 與 -path 一樣,只是參數為正規表示式
find . -regex ".*/e.*h$"
# 忽略大小寫
find . -iregex ".*/e.*h$"
# 否定參數(獨立參數,可以配合 -name/path/regex 等用)
find . ! -iregex ".*/e.*h$"
# 例如排除路徑含有 node_modules 的
find ../tnode ! -regex ".*node_modules.*"
# 指定目錄深度,-maxdepth 1 表示向下找 1 級(也就是 .. 的孩子,不找孫子)
find .. -name "*.js" -maxdepth 1
# 也可以指定起始深度,-mindepth 2 -maxdepth 2 表示只在 .. 的孫子中找,不找兒子也不找孫子的兒子
find .. -name "*.js" -mindepth 2 -maxdepth 2
# 單獨用 -mindepth 找超過指定深度的檔案(找深路徑 lib)
find .. -regex ".*node_modules*.*.js$" -mindepth 20
按檔案類型搜尋
# 指定檔案/資料夾,-type f 表示只輸出檔案
find ../tnode ! -regex ".*node_modules.*" -type f
P.S. 參數順序會影響搜尋效率,比如先檢查深度再過濾類型更快
檔案類型與 type 參數值對應關係:
普通檔案:f
符號連結:l
目錄:d
面向字元的裝置檔案:c
面向區塊的裝置檔案:b
通訊端 (Socket):s
FIFO:p
按時間搜尋
每個檔案有 3 種時間戳記:
存取時間:-atime
修改時間:-mtime
狀態改變時間:-ctime
參數值為整數,表示天數,可以前綴 + 和 -,分別表示大於、小於,例如:
# 找出父級目錄中,昨天到現在存取過的檔案
find .. -type f -atime -1
也有以分鐘為單位的:
# -amin, -mmin, -cmin
find .. -type f -amin $((-1 * 60 * 24))
還可以指定一個檔案作為參照,找出更新的(修改時間更近的)檔案:
# 找出父級目錄中,比 ~/.bash_profile 更新的檔案
find .. -type f -newer ~/.bash_profile
按檔案大小搜尋
# 目前目錄下大於 1K 的檔案
find . -type f -size +1k
支援 b 區塊, c 位元組, w 字, k, M, G 單位,注意前面小寫,後兩個大寫,在其他命令裡一般也是這樣,例如 split
其他用法
# 查找並刪除
find . -type f -name "*.tmp" -delete
# 匹配檔案權限
find . -type f -perm 777 -print
find . -type f -user ayqy
與 -exec 結合執行其他命令
# 查找並格式化輸出
find . -type f -exec printf "file: %s\n" {} \;
# 查找並備份
find . -type f -mtime +7 -exec cp {} bak/ \;
P.S. 末尾跳脫分號用來表示 -exec 參數值結束,必須要有
-exec 只能執行一條命令,需要執行多條的話,把命令寫入檔案再執行,例如把備份命令寫入 bak.sh :
#!/bin/bash
BAK_DIR=bak
if ! test -e "$BAK_DIR";
then
mkdir "$BAK_DIR"
fi
for file in "$@";
do
cp $file "$BAK_DIR"
done
再查找執行:
find . -type f -mtime +7 -exec ./bak.sh {} \;
-prune 排除不需要查找的東西:
# 跳過 .git 和 node_modules 目錄
find . \( -name ".git" -prune \) -o \( -name "node_modules" -prune \) -o \( -type f -print \)
xargs
xargs 命令把從 stdin 接到的數據重新格式化,作為參數提供給其他命令,緊跟在管道操作符之後,基本形式:
cmd | xargs
把多行輸入轉換成單行輸出:
# 把換行字元換成空格
cat test.sh | xargs
把單行輸入轉換成多行輸出:
# 按每行參數數量斷開
echo "1 22 3 4 5 6 7" | xargs -n 3
-d 指定分隔符,實現字串 split :
# split
echo "1,2,3,4" | xargs -d ,
# `-d` 參數是 GNU 擴充,FreeBSD 和 Mac 上沒有,用其他方法完成
echo "1,2,3,4" | tr , ' '
-I 指定替換字串:
# replace
echo "1 2 3 4" | xargs -n 1 -I {} find {}.txt
find 結合 xargs :
# 查找並刪除
find . -type f -name "*.tmp" -print0 | xargs -0 rm -f
這裡的 -print0 和 xargs -0 用 \0 作為分隔符,避免 temp file.tmp 之類的含有預設分隔符的檔案名稱被拆成兩個參數
統計程式碼行數:
find . -type f -name "*.sh" -print0 | xargs -0 wc -l
對一個參數執行多條命令:
# 與上面的 replace 作用相同
echo '1\n2\n3\n4' | (while read arg; do find $arg.txt; done)
xargs 對每個參數只能執行一條命令,改用子 shell 中迴圈讀取的話,能在迴圈體裡執行多條命令
P.S. 這裡的括號是圓括號擴充運算子,開子 shell 執行括號裡的命令,不是前面的條件分組,不要跳脫括號
tr(translate)
對來自標準輸入的字元進行替換、刪除和壓縮,用來做字串處理
# 大小寫轉換
echo 'Ho Hoho hoho' | tr 'a-z' 'A-Z'
如果兩個字元集合大小不一樣,就把後一個集合用其最後一個字元補足,例如:
# 結果是 ABC XXX
echo 'abc xyz' | tr 'a-z' 'A-X'
P.S. 定義字元集合的形式是「起始字元-終止字元」,結果不是一個連續的字元序列的話,就當作 3 個普通字元
注意:tr 只是對輸入的每個字元做映射,沒有字串匹配和替換,是字元級的操作,不是字元序列(字串)級的
其他常用選項:
# -d 刪除字元
# 結果是 a, a , 579
echo 'hohoa, hoa 123, 4579' | tr -d 'ho0-4'
# -c 得到補集,一般與 -d 結合刪除補集裡的字元,只保留給定字元集合裡的
# 結果是 hohoho1234
echo 'hohoa, hoa 123, 4579' | tr -d -c 'ho0-4'
# -s 壓縮字元(把連續的重複字元換成一個)
# 結果是 ha, ha
echo 'hhhhhha, ha' | tr -s 'a-z'
用字元類別(character class)作為集合:
# 大小寫轉換
echo '124abcX1' | tr '[:lower:]' '[:upper:]'
其他字元類別可以透過 man tr 查看
md5sum, sha1sum
這兩個命令用來計算校驗和,例如:
# 求檔案 md5
# 結果是 `32 個字元的 16 進位字串 檔案名稱`
md5sum test.sh
P.S. Mac 預設沒有 md5sum, sha1sum,需要額外安裝
用 md5 檔案校驗
# 用 md5 檔案檢查檔案是否正確
md5sum -c file.md5
用 md5deep 生成資料夾的 md5,需要額外安裝(sha1deep 與之類似):
# yum 安裝
yum install md5deep
# 求資料夾的 md5
# -r 遞迴,-l 生成相對路徑(預設是絕對路徑)
md5deep -rl dir > dir.md5
# 用所有 md5 檔案校驗
md5sum *.md5
sort & uinq
sort 命令對行排序,uniq 去重,一般配合使用,例如:
# 對 file.txt 內容每行按字典序排序,並去重
sort file.txt | uniq
# 或者
sort -u file.txt
預設按字典序升序排序,-n 按數值排序,-r 降序:
# 如果字母數字都有,字母在前
sort -n file.txt
sort -r file.txt
其他常用選項:
# 檢查檔案內容是否有序,是否按數值序用 -nC
# 返回值為 0,表示有序
sort -C file.txt; echo $?
# 按第 2 列排序
sort -k 2 file.txt
# 按第 2 個字元到第 5 個字元排序
sort -k 2,5 file.txt
# 用 \0 作為分隔符(透過管道結合其他命令時有用)
sort -z file.txt
# 忽略前導空白字元
sort -b file.txt
uniq 命令只能用於有序的輸入,所以一般結合 sort 使用:
# 只顯示唯一的行(出現多於 1 次的行都被濾掉)
uniq -u sorted.txt
# 統計各行出現次數
uniq -c sorted.txt
# 找出重複的行
uniq -d sorted.txt
去重也可以指定 key :
# -s 跳過前幾個字元,-w 指定 key 的長度
uniq -s 3 -w 2 sorted.txt
P.S. Mac 沒有 -w 選項
split
split 命令用來分割大檔案,例如:
# 把 data.txt 分割成 1k 的多個檔案
split -b 1k data.txt
預設生成 xaa, xab, xac... 之類的檔案名稱,預設嚴格按大小拆分,行可能會被截斷,甚至一個漢字被拆開
生成的檔案名稱可以手動指定,最後一個參數是前綴,預設是 x , -a 指定後綴長度,其他選項請查看 man split
也可以按行分割檔案:
# 每個檔案 10 行,檔案名稱為 `small.aa, small.ab, small.ac...`
split -l 10 test.sh 'small.'
P.S. 原來有這種命令,當時為了拆分 sql 備份檔案,特意找了一個能夠編輯大檔案的文字編輯器,手動分割的...
P.S. 另一個更強大的檔案分割命令是 csplit ,常用來分割日誌檔案,能夠以是否存在指定文字內容為條件拆分
其他小技巧
暫存檔案命名
Ubuntu, Debian 中有 tempfile 命令,用來生成暫存檔案名稱(一個隨機字串),其他環境可以使用 RANDOM 環境變數,或者目前程序 ID:
# 取 RANDOM 環境變數的值
$RANDOM
# 取目前程序 ID
$$
字串提取
%, %%, #, ## 運算子提供了強大的字串提取功能:
file=logo.png
# 提取檔案名稱
filename=${file%.*}
echo filename:$filename
# 提取副檔名
ext=${file##*.}
echo ext:$ext
用法如下:
# 從 var 的值中刪掉 % 右側萬用字元所匹配的字串,從右邊向左匹配
${var%.*}
# %% 貪婪匹配,會找出最長字串,% 匹配最短字串
${var%.*}
# 從 var 的值中刪掉 # 右側萬用字元所匹配的字串,從左向右匹配
${var#*.}
# 對應的貪婪匹配
${var##*.}
提取副檔名應該用 ## 貪婪匹配,因為 file.txt.md5 之類的檔案名稱含有多個 .
暫無評論,快來發表你的看法吧