2017-06-18 72 views

回答

3

使用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選項。

+0

酷特徵;我不知道這件事。似乎是最乾淨和最有效的解決方案。作爲獎勵,當文本文件列出了我自己的答案中討論的模式本身時,它也可以工作。 (希望這是期望的行爲。) – 5gon12eder

2

這是將在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中,您還可以應用全局模式而不是單個文件名,例如*.jpgmy*.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進程的替換,如果文件名包含新行,將會中斷。

1

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.*到文件中保持的列表,如果你想處理那些額外的邏輯是必需的。

總體而言,指定哪些文件到刪除似乎有點危險,因爲錯誤是不好的一面

1

只要沒有空格或特殊的轉義字符的文件名,其中任一(或這些變化)會工作:

  1. rm -v $(stat -c %n * | sort excluded_file_list | uniq -u)

  2. stat -c %n * | grep -vf excluded_file_list | xargs rm -v

相關問題