一、行排序
1.1 sort
1.1.1 排序一組文件(例如file1.txt和file2.txt)
$ sort file1.txt file2.txt > sorted.txt
或是
$ sort file1.txt file2.txt -o sorted.txt
1.1.2 按照數字順序排序
$ sort -n file.txt
1.1.3 按照數字順序排序
$ sort -r file.txt
1.1.4 按照月份排序(依照一月、二月、三月……)
$ sort -M months.txt
1.1.5 合併兩個已排序過的文件
$ sort -m sorted1 sorted2
1.1.6 找出已排序文件中不重複的行
$ sort file1.txt file2.txt | uniq
1.1.7 檢查文件是否已經排序過
#!/bin/bash
#功能描述:排序
sort -C filename ;
if [ $? -eq 0 ]; then
echo Sorted;
else
echo Unsorted;
fi
如果文件已經排序,sort會返回爲0的退出碼($?),否則返回非0。
1.1.8 依據列排序
如果輸入數據的格式如下,我們可以按列排序:
$ cat data.txt
1 mac 2000
2 winxp 4000
3 bsd 1000
4 linux 1000
-k指定了排序所依據的字符。-k後的整數指定了文本文件中的某一列。列與列之間由空格分隔。如果是單個數字,則指的是列號。-r告訴sort命令按照逆序進行排序。例如:
# 依據第1列,以逆序形式排序
$ sort -nrk 1 data.txt
4 linux 1000
3 bsd 1000
2 winxp 4000
1 mac 2000
# -nr表明按照數字順序,採用逆序形式排序
# 依據第2列進行排序
$ sort -k 2 data.txt
3 bsd 1000
4 linux 1000
1 mac 2000
2 winxp 4000
1.1.9 輸出與以\0作爲終止符的xargs命令相兼容
$ sort -z data.txt | xargs -0
# 終止符\0用來確保安全地使用xargs命令
1.1.10 忽略標點符號並以字典序排序
$ sort -bd unsorted.txt
其中,選項-b用於忽略文件中的前導空白行,選項-d用於指明以字典序進行排序。
1.2 uniq
uniq命令可以從給定輸入中(stdin或命令行參數指定的文件)找出唯一的行,報告或刪除那些重複的行。uniq只能作用於排過序的數據,因此,uniq通常都與sort命令結合使用。
1.2.1 生成唯一的行(打印輸入中的所有行,但是其中重複的行只打印一次)
cat sorted.txt
bash
foss
hack
hack
$ uniq sorted.txt
bash
foss
hack
或是
$ sort unsorted.txt | uniq
1.2.2 只顯示唯一的行
$ uniq -u sorted.txt
bash
foss
或是
$ sort unsorted.txt | uniq -u
1.2.3 統計各行在文件中出現的次數
$ sort unsorted.txt | uniq -c
1 bash
1 foss
2 hack
1.2.4 找出文件中重複的行
$ sort unsorted.txt | uniq -d
hack
1.2.5 輸出與以\0作爲終止符的xargs命令相兼容
下面的命令將刪除所有指定的文件,這些文件的名字是從files.txt中讀取的:
$ uniq -z file.txt | xargs -0 rm
如果某個文件名出現多次,uniq命令只會將這個文件名寫入stdout一次,這樣就可以避免出現rm: cannot remove FILENAME: No such file or directory。
二、臨時文件命名與隨機數
shell腳本經常需要存儲臨時數據。最適合存儲臨時數據的位置是 /tmp(該目錄中的內容在系統重啓後會被清空)
2.1 mktemp
mktemp命令可以爲臨時文件或目錄創建唯一的名字。
2.1.1 創建臨時文件
$ filename=`mktemp`
$ echo $filename
/tmp/tmp.8xvhkjF5fH
上面的代碼創建了一個臨時文件,然後打印出保存在變量filename中的文件名。
2.1.2 創建臨時目錄
$ dirname=`mktemp -d`
$ echo $dirname
tmp.NI8xzW7VRX
上面的代碼創建了一個臨時目錄,然後打印出保存在變量dirname中的目錄名。
2.1.3 僅生產文件名,不創建文件
$ tmpfile=`mktemp -u`
$ echo $tmpfile
/tmp/tmp.RsGmilRpcT
文件名被存儲在$tmpfile中,但並沒有創建對應的文件。
2.1.4 基於模板創建臨時文件名
$mktemp test.XXX
test.2tc
如果提供了定製模板,X會被隨機的字符(字母或數字)替換。注意,mktemp正常工作的前提是保證模板中至少要有3個X。
三、分割文件與數據
3.1 split
split命令可以用來分割文件。該命令接受文件名作爲參數,然後創建出一系列體積更小的
文件,其中依據字母序排在首位的那個文件對應於原始文件的第一部分,排在次位的文件對應於原始文件的第二部分,以此類推。
3.1.1 指定分割大小
$ split -b 10k data.file
$ ls
data.file xaa xab xac xad xae xaf xag xah xai xaj
上面的命令將data.file分割成了10個大小爲10KB的文件。這些新文件以xab、xac、xad的形式命名。在split命令中,除了k(KB),我們還可以使用M(MB)、G(GB)、c(byte)和w(word)。
3.1.2 使用數字後綴
$ split -b 10k data.file -d -a 4
$ ls
data.file x0009 x0019 x0029 x0039 x0049 x0059 x0069 x0079
-a length可以指定後綴長度。
3.1.3 爲分割後的文件指定文件名前綴
之前那些分割後的文件名都是以x作爲前綴。如果要分割的文件不止一個,我們自然希望能自己命名這些分割後的文件,這樣才能夠知道這些文件分別屬於哪個原始文件。這可以通過提供一個前綴作爲最後一個參數來實現。
這次我們使用split_file作爲文件名前綴,重新執行上一條命令:
$ split -b 10k data.file -d -a 4 split_file
$ ls
data.file split_file0002 split_file0005 split_file0008
strtok.c
split_file0000 split_file0003 split_file0006 split_file0009
split_file0001 split_file0004 split_file0007
3.1.4 根據行數來分割文件
# 分割成多個文件,每個文件包含10行
$ split -l 10 data.file
3.2 csplit
csplit實用工具能夠基於上下文來分隔文件。它依據的是行計數或正則表達式。這個工具對於日誌文件分割尤爲有用。
$ cat server.log
SERVER-1
[connection] 192.168.0.1 success
[connection] 192.168.0.2 failed
[disconnect] 192.168.0.3 pending
[connection] 192.168.0.4 success
SERVER-2
[connection] 192.168.0.1 failed
[connection] 192.168.0.2 failed
[disconnect] 192.168.0.3 success
[connection] 192.168.0.4 failed
SERVER-3
[connection] 192.168.0.1 pending
[connection] 192.168.0.2 pending
[disconnect] 192.168.0.3 pending
[connection] 192.168.0.4 failed
我們需要將這個日誌文件分割成server1.log、server2.log和server3.log,這些文件的內容分別取自原文件中不同的SERVER部分。實現方法如下:
$ csplit server.log /SERVER/ -n 2 -s {*} -f server -b "%02d.log"
$ rm server00.log
$ ls
server01.log server02.log server03.log server.log
下面是這個命令的詳細說明。
/SERVER/ 用來匹配特定行,分割過程即從此處開始。
/[REGEX]/ 用於描述文本模式。它從當前行(第一行)一直複製到(但不包括)包含SERVER
的匹配行。
{*} 表示根據匹配重複執行分割操作,直到文件末尾爲止。可以用{整數}的形式來指定分
割執行的次數。
-s 使命令進入靜默模式,不打印其他信息。
-n 指定分割後的文件名後綴的數字個數,例如01、02、03等。
-f 指定分割後的文件名前綴(在上面的例子中,server就是前綴)。
-b 指定後綴格式。例如%02d.log,類似於C語言中printf的參數格式。在這裏:文件
名 = 前綴 + 後綴,也就是server + %02d.log。
因爲分割後得到的第一個文件沒有任何內容(匹配的單詞就位於文件的第一行中),所以我們刪除了server00.log。
四、根據擴展名切分文件名
4.1 提取文件名及擴展名
4.1.1 %操作符提取文件名
藉助%操作符可以從name.extension這種格式中提取name部分(文件名)。下面的例子從sample.jpg中提取了sample:
file_jpg="sample.jpg"
name=${file_jpg%.*}
echo File name is: $name
輸出結果:
File name is: sample
${VAR%.*} 的含義如下:
a.從 $VAR中刪除位於%右側的通配符.*所匹配的字符串。通配符從右向左進行匹配。
b.給VAR賦值,即VAR=sample.jpg。通配符從右向左匹配到的內容是.jpg,得到輸出sample。
%屬於非貪婪(non-greedy)操作。它從右向左找出匹配通配符的最短結果。還有另一個操作符%%,它與%相似,但行爲模式卻是貪婪的,這意味着它會匹配符合通配符的最長結果。例如,我們現在有這樣一個文件:
VAR=hack.fun.book.txt
使用%操作符從右向左執行非貪婪匹配,得到匹配結果.txt:
$ echo ${VAR%.*}
命令輸出:hack.fun.book。
使用%%操作符從右向左執行貪婪匹配,得到匹配結果.fun.book.txt:
$ echo ${VAR%%.*}
命令輸出:hack。
4.1.2 #操作符提取後綴名
#操作符可以提取出擴展名。
提取文件名中的 .jpg並存儲到變量file_jpg中:
extension=${file_jpg#*.}
echo Extension is: jpg
輸出結果:
Extension is: jpg
${VAR%.} 的含義如下:
從VAR中刪除位於#右側的通配符(即在上例中使用的.)從左向右所匹配到的字
符串。
和%%類似,#也有一個對應的貪婪操作符##。
4.1.2 %、%% 與#、##
這裏有個能夠提取域名中不同部分的實例。假定URL爲www.google.com
$ echo ${URL%.*} # 移除.*所匹配的最右邊的內容
www.google
$ echo ${URL%%.*} # 將從右邊開始一直匹配到最左邊的.*(貪婪操作符)移除
www
$ echo ${URL#*.} # 移除*.所匹配的最左邊的內容
google.com
$ echo ${URL##*.} # 將從左邊開始一直匹配到最右邊的*.(貪婪操作符)移除
com
五、多個文件的重命名與移動
5.1 rename
5.1.1 實例
下面的腳本利用find查找PNG和JPEG文件,然後使用##操作符和mv將查找到的文件重命名爲image-1.EXT、image-2.EXT等。注意,腳本並不會修改文件的擴展名:
#!/bin/bash
#文件名: rename.sh
#用途: 重命名 .jpg 和 .png 文件
count=1;
for img in `find . -iname '*.png' -o -iname '*.jpg' -type f -maxdepth 1`
do
new=image-$count.${img##*.}
echo "Renaming $img to $new"
mv "$img" "$new"
let count++
done
輸出如下:
$ ./rename.sh
Renaming hack.jpg to image-1.jpg
Renaming new.jpg to image-2.jpg
Renaming next.png to image-3.png
該腳本重命名了當前目錄下所有的.jpg和.png文件,新文件名採用形如image-1.jpg、image-2.jpg、image-3.png、image-4.png的格式。
5.1.2 將文件名中的空格替換成字符 “_”
$ rename 's/ /_/g' *
's/ /_/g’用於替換文件名,而 * 是用於匹配目標文件的通配符,它也可以寫成 *.txt
或其他通配符模式。
5.1.3 轉換文件名的大小寫
$ rename 'y/A-Z/a-z/' *
$ rename 'y/a-z/A-Z/' *
5.1.4 將所有的.mp3文件移入給定的目錄
$ find path -type f -name "*.mp3" -exec mv {} target_dir \;
5.1.5以遞歸的方式將所有文件名中的空格替換爲字符"_"
$ find path -type f -exec rename 's/ /_/g' {} \;
六、拼寫檢查與詞典操作
6.1 詞典文件
目錄/usr/share/dict/中包含了一些詞典文件。所謂“詞典文件”就是包含了單詞列表的文本文
件。我們可以利用它來檢查某個單詞是否在詞典之中。
$ ls /usr/share/dict/
american-english british-english
爲了檢查給定的單詞是否爲詞典單詞,可以使用下面的腳本:
#!/bin/bash
#文件名: checkword.sh
word=$1
grep "^$1$" /usr/share/dict/british-english -q
if [ $? -eq 0 ]; then
echo $word is a dictionary word;
else
echo $word is not a dictionary word;
fi
這個腳本的用法如下:
$ ./checkword.sh ful
ful is not a dictionary word
$ ./checkword.sh fool
fool is a dictionary word
在grep中,^標記着單詞的開始,$標記着單詞的結束①,-q選項 禁止grep產生任何輸出。
6.2 aspell
用拼寫檢查命令aspell來覈查某個單詞是否在詞典中:
#!/bin/bash
#文件名: aspellcheck.sh
word=$1
output=`echo \"$word\" | aspell list`
if [ -z $output ]; then
echo $word is a dictionary word;
else
echo $word is not a dictionary word;
fi
當給定的輸入不是一個詞典單詞時,aspell list命令會生成輸出,否則不產生任何輸出。
-z用於確認$output是否爲空。
6.3 look
look命令可以顯示出以特定字符串起始的行。你可以用它在日誌文件中查找以特定日期爲
首的記錄,或是在詞典中查找以特定字符串開頭的單詞。
look默認會搜索/usr/share/dict/words,你也可以給出文件供其搜索:
$ look word
或者使用
$ grep "^word" filepath
例如:
$ look android
android
android's
androids
在/var/log/syslog中找出以特定日期起始的日誌記錄
$look 'Aug 30' /var/log/syslog
七、利用並行進程加速命令執行
如果多個文件需要生成校驗和,我們可以使用下面的腳本來運行md5sum的多個實例:
#/bin/bash
#文件名: generate_checksums.sh
PIDARRAY=()
for file in File1.iso File2.iso
do
md5sum $file &
PIDARRAY+=("$!")
done
wait ${PIDARRAY[@]}
運行腳本後,可以得到如下輸出:
$ ./generate_checksums.sh
330dcb53f253acdf76431cecca0fefe7 File1.iso
bd1694a6fe6df12c3b8141dcffaf06e6 File2.iso
輸出結果和下面命令的結果一樣:
md5sum File1.iso File2.iso
我們利用了Bash的操作符&,它使得shell將命令置於後臺並繼續執行腳本。這意味着一旦循環結束,腳本就會退出,而md5sum進程仍在後臺運行。爲了避免這種情況,我們使用!保存着最近一個後臺進程的PID。我們將這些PID放入數組,然後使用wait命令等待這些進程結束。
八、檢查目錄以及其中的文件與子目錄
8.1 生成目錄的樹狀視圖
下面來查看目錄/var/log的樹狀視圖:
$ cd /var/log
$ find . -exec sh -c 'echo -n {} | tr -d "[:alnum:]_.\-" | \
tr "/" " "; basename {}' \;
生成如下輸出:
mail
statistics
gdm
::0.log
::0.log.1
cups
error_log
access_log
... access_l
-exec選項創建了一個子shell,在這個子shell中使用echo命令將文件名發送給tr命令的
stdin。這裏用到了兩個tr命令。第一個tr刪除了所有的字母數字字符、連字符(-)、下劃線(_)和點號(.),只將路徑中的斜線(/)傳入第二個tr,後者將這些斜線全部轉換成空格。最後,利用basename命令去掉文件名前的路徑部分並將結果顯示出來。
8.2 生成文件及子目錄的彙總信息
下面的命令可以獲得當前目錄下文件的彙總信息:
for d in `find . -type d`;
do
echo `find $d -type f | wc -l` files in $d;
done
如果在/var/log下執行該腳本,會生成如下輸出:
103 files in .
17 files in ./cups
0 files in ./hp
0 files in ./hp/tmp