利用 find 搜尋指定檔案並批次刪除,使用 array 陣列

前篇「利用 find 搜尋指定檔案並批次刪除」提到,可以使用 array 陣列來處理「同時多個搜尋條件,但執行同一個指令」的情況,然而這得對 bash script 程式碼編寫,和 array 陣列有一定程度的了解,所以就另起一篇了。

前篇利用了四道命令:(底下為測試所需,把根目錄 / 改成當前目錄 ./)

find ./ -type f -iname "*.iso" 2>null -exec rm -f {} +
find ./ -type f -iname "*.zip" 2>null -exec rm -f {} +
find ./ -type f -iname "*.rar" 2>null | xargs rm -f {} +
find ./ -type f -iname "*.rpm" 2>null | xargs rm -f {} +

這四道命令雷同,後半段使用參數[ -exec rm ]和通道[ | xargs rm ]的功能一樣,只有 “搜尋字串” 的差異,因此,若將搜尋字串設成變數(例如:part),就能簡化命令成一行:

find ./ -type f -iname "*.$part*" 2>/dev/null -exec rm -f {} +

明白了這點,就能開始思考怎麼把搜尋字串一一放進變數裡了。我常用的方法有二:
1. shift(參數位移)
2. array(陣列取值)

閒話休提,直接寫程式吧!

首先建立一個 bash 可執行檔,開啟慣用的文書編輯器,在 ~/bin 建立一個新文件(或是 PATH 環境變數指定目錄),另存為 xxx.sh。然後就可以開始編寫執行程序了。為免測試過程出錯,暫時只用前半段 find 指令字串搜尋這個部分來示範。

一、使用 shift 參數位移

本篇主題是 array 陣列,因此對 shift 就不多言。sh 文件內容是這樣的:

until [ $# -eq 0 ]    #執行到沒有參數為止
do
    find ./ -type f -iname "*$1*" 2>/dev/null
    shift    #位移一個參數
done

完成後,在終端機執行此 sh 檔,後面加上所有要搜尋的字串:

bash xxx.sh .iso .rar .zip .rpm

結果:

./archlinux-2018.01.01-x86_64.iso
./USBOXv3.iso
./Boot_Tools.iso
./two/archlinux-2018.01.01-x86_64.iso
./two/conky_cronograph_station_by_hfcf-d4lwd83.rar
./two/USBOXv3.rar
./one/three/Disk2vhd.zip
./two/transgui-code-976-tags-5.0.1.zip
./two/ACS-Unified-PKG-Lnx-113-P.zip
./flash-player-npapi-28.0.0.126-release.x86_64.rpm
./one/fpc-3.0.4-1.x86_64.rpm
./one/lazarus-1.8.0-0.x86_64.rpm
./one/fpc-src-3.0.4-1.x86_64.rpm
./one/three/vlc-codecs-2.2.6-5.3.x86_64.rpm
./vlc-codecs-2.2.6-5.3.x86_64.rpm

核對搜尋結果,能看到的確依照參數順序一一執行。

二、使用 array 陣列取值

1. 首先指定陣列內容,全部放進(…)中,以半形空白隔開
arr=(.iso .rar .zip .rpm)

2. 接著取得陣列長度
length=${#arr[*]}

3. 以陣列長度決定迴圈執行次數
for(( i=0; i<$length; i++ ))
do
......
done

4. 以 part 或其它自訂變數取得搜尋字串
part="${arr[$i]}"

若要確認則可先以[ echo “$part” ]來顯示取得的字串值,以供核對;無誤後再以 # 符號 mark 掉。

5. 開始執行命令
find ./ -type f -iname "*$part*" 2>/dev/null


總結起來,傳統語法的程式碼如下:

arr=(.iso .rar .zip .rpm)
length=${#arr[*]}
for(( i=0; i<$length; i++ ))
do
    part="${arr[$i]}"
    find ./ -type f -iname "*$part*" 2>/dev/null
done

更簡潔的語法如下:

arr=(.iso .rar .zip .rpm)
for i in ${!arr[*]}
do
    find ./ -type f -iname "*${arr[$i]}*" 2>/dev/null
done

當然,還是建議使用[ part=”${arr[$i]}” ]來取值比較好,這樣在後續運用時比較直覺。

到這裡,應該就能加上執行刪除的指令了,把後半段[ -exec rm ]給加上去,享受一旦出錯就欲哭無淚的刪除快感吧!……什麼?不想再動用 testdisk 這類反刪除救援程式?……先跟你握個手,我也是受夠了,所以才在程式裡加上進一步確認動作的功能。

三、以 case 加上動作確認

這是因為個人在嘗試過程,經常因為下指令時沒有加上確認動作(以參數或其它方式),導致各種悲哀情事,在自動執行的迴圈中尤其如此(所謂一發不可收拾,一回頭已百年身)。因此若要在 sh 文件中執行像 rm 這類有嚴重傷害性的指令,我習慣加上確認程序。

要怎麼確認呢?讓程式停下來問我們要不要做就行了,要就做,不要就取消。在 bash 中,這種事讓 case 來做就行了。不廢話,直接寫程式碼:

find -iname "*$part*"    #先顯示一次搜尋結果以供確認

echo
echo "要刪除這些 $part 檔案嗎? ( y/n )"
read SEL
case $SEL in
    Y | y ) find -iname "*$part*" -exec rm -f {} +;;
    * ) ;;
esac
done

結果:

./archlinux-2018.01.01-x86_64.iso
./USBOXv3.iso
./Boot_Tools.iso
./two/archlinux-2018.01.01-x86_64.iso

要刪除這些 .iso 檔案嗎? ( y/n )

./two/conky_cronograph_station_by_hfcf-d4lwd83.rar
./two/USBOXv3.rar

要刪除這些 .rar 檔案嗎? ( y/n )

./one/three/Disk2vhd.zip
./two/transgui-code-976-tags-5.0.1.zip
./two/ACS-Unified-PKG-Lnx-113-P.zip

要刪除這些 .zip 檔案嗎? ( y/n )

./flash-player-npapi-28.0.0.126-release.x86_64.rpm
./one/fpc-3.0.4-1.x86_64.rpm
./one/lazarus-1.8.0-0.x86_64.rpm
./one/fpc-src-3.0.4-1.x86_64.rpm
./one/three/vlc-codecs-2.2.6-5.3.x86_64.rpm
./vlc-codecs-2.2.6-5.3.x86_64.rpm

要刪除這些 .rpm 檔案嗎? ( y/n )

除了 Y 鍵之外,其它按鍵都會取消動作(包括 Enter)跳到下一個迴圈,這樣,或許能將悲哀的傷亡情事降到最低,晚上比較睡得著覺吧。

若要問為什麼要先執行一次 find 顯示結果,再執行一次 find 執行刪除,這樣不是多此一舉嗎?呵呵,反正久久才做一次的指令功能,不想浪費心力思索怎樣做到簡單又精美,這事留給看倌去努力吧!

====================

# 【操作環境】

# 系統:openSUSE Leap 42.3 KDE Plasma 5
# Shell Script:GNU bash, version 4.3.42
# 終端機:Konsole 版本 17.04.2

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *