我有一個txt文件,其中包含文件名列表shell腳本刪除其文件名不是在文本文件中
例子:
10.jpg
11.jpg
12.jpeg
...
在這個文件應該從刪除過程保護文件夾其他文件應該刪除。
所以我想這個問題的oppposite邏輯:Shell command/script to delete files whose names are in a text file
如何做到這一點?
我有一個txt文件,其中包含文件名列表shell腳本刪除其文件名不是在文本文件中
例子:
10.jpg
11.jpg
12.jpeg
...
在這個文件應該從刪除過程保護文件夾其他文件應該刪除。
所以我想這個問題的oppposite邏輯:Shell command/script to delete files whose names are in a text file
如何做到這一點?
使用extglob
和Bash擴展模式匹配!(pattern-list)
:
!(模式列表)
匹配除給定模式之一以外的任何內容
其中模式列表是由|分隔的一個或多個模式的列表。extglob
如果設置,則啓用上述擴展模式匹配特徵。
因此,例如:
$ ls
10.jpg 11.jpg 12.jpeg 13.jpg 14.jpg 15.jpg 16.jpg a.txt
$ shopt -s extglob
$ shopt | grep extglob
extglob on
$ cat a.txt
10.jpg
11.jpg
12.jpeg
$ tr '\n' '|' < a.txt
10.jpg|11.jpg|12.jpeg|
$ ls !(`tr '\n' '|' < a.txt`)
13.jpg 14.jpg 15.jpg 16.jpg a.txt
刪除的文件根據例子是。
因此,使用extglob
和!(pattern-list)
,我們可以獲得基於文件內容排除的文件。
此外,如果要排除以.
開頭的條目,則可以使用shopt -s dotglob
打開dotglob
選項。
這是將在bash GLOBIGNORE工作的一種方式:
$ cat file2
10.jpg
11.jpg
12.jpg
$ ls *.jpg
10.jpg 11.jpg 12.jpg 13.jpg
$ echo $GLOBIGNORE
$ GLOBIGNORE=$(tr '\n' ':' <file2)
$ echo $GLOBIGNORE
10.jpg:11.jpg:12.jpg:
$ ls *.jpg
13.jpg
因爲很明顯,無論globing(文件,模式等)列入GLOBIGNORE bash的變量忽略。
這就是爲什麼最後的ls
只報告文件13.jpg
,因爲文件10,11和12.jpg被忽略。
由於使用rm *.jpg
只會13.jpg
在我的系統中刪除結果:
$ rm -iv *.jpg
rm: remove regular empty file '13.jpg'? y
removed '13.jpg'
當你完成,你可以設置GLOBIGNORE爲null:
$ GLOBIGNORE=
一提的實在值得,那在GLOBIGNORE中,您還可以應用全局模式而不是單個文件名,例如*.jpg
或my*.mp3
等。
備選:
我們可以用編程技術(的grep,awk中,等)存在於ignorefile的文件名和文件進行比較當前目錄下:
$ awk 'NR==FNR{f[$0];next}(!($0 in f))' file2 <(find . -type f -name '*.jpg' -printf '%f\n')
13.jpg
$ rm -iv "$(awk 'NR==FNR{f[$0];next}(!($0 in f))' file2 <(find . -type f -name '*.jpg' -printf '%f\n'))"
rm: remove regular empty file '13.jpg'? y
removed '13.jpg'
注:這也使得使用bash進程的替換,如果文件名包含新行,將會中斷。
George Vasiliou的答案的另一種替代方法是用文件名讀取該文件以繼續使用Bash內建的mapfile
,然後檢查每個要刪除的文件是否在該列表中。
#! /bin/bash -eu
mapfile -t keepthose <keepme.txt
declare -a deletethose
for f in "[email protected]"
do
keep=0
for not in "${keepthose[@]}"
do
[ "${not}" = "${f}" ] && keep=1 || :
done
[ ${keep} -gt 0 ] || deletethose+=("${f}")
done
# Remove the 'echo' if you really want to delete files.
echo rm -f "${deletethose[@]}"
的-t
選項使得mapfile
從它從文件中讀出線修整後換行符。雖然沒有其他的空白區域會被修剪。如果你的文件名實際上包含空格,這可能就是你想要的,但是如果有人在他們想保留的重要文件的名字之前或之後不小心插入了一個空格,它也會引起微妙的意外。
請注意,我首先構建應刪除的文件列表,然後將它們全部刪除,而不是單獨刪除每個文件。這節省了一些子流程調用。
在列表中的查找,如上面所編碼的,具有線性這給整個腳本二次複雜性的複雜性(精確地說,Ñ × 中號其中Ñ是命令行參數的數量和中號keepme.txt
文件中的條目數)。如果你只有幾十個文件,這應該沒問題。不幸的是,我不知道有更好的方法來檢查Bash中的設置成員資格。 (我們不能將文件名用作關聯數組中的鍵,因爲它們可能不是合適的標識符。)如果您關心的是許多文件的性能,那麼使用更強大的Python語言可能是值得考慮的。
我還想提一下,上面的例子只是比較字符串。它不會意識到important.txt
和./important.txt
是相同的文件,因此刪除該文件。在比較之前使用readlink -f
將文件名轉換爲規範路徑會更加健壯。
此外,用戶可能希望能夠把globing模式(如important.*
到文件中保持的列表,如果你想處理那些額外的邏輯是必需的。
總體而言,指定哪些文件到不刪除似乎有點危險,因爲錯誤是不好的一面
只要沒有空格或特殊的轉義字符的文件名,其中任一(或這些變化)會工作:
rm -v $(stat -c %n * | sort excluded_file_list | uniq -u)
stat -c %n * | grep -vf excluded_file_list | xargs rm -v
酷特徵;我不知道這件事。似乎是最乾淨和最有效的解決方案。作爲獎勵,當文本文件列出了我自己的答案中討論的模式本身時,它也可以工作。 (希望這是期望的行爲。) – 5gon12eder