2017-03-16 104 views
3

我需要創建一個bash腳本,它將刪除當前文件夾中除「文件」中提到的所有文件(「.rmignore」)以外的所有文件。刪除除配置文件中提到的所有文件

該文件可能包含與當前文件夾相關的地址,該文件夾也可能包含星號(*)。

例如:

1.php 
2/1.php 
1/*.php 

我已經在努力與GLOBIGNORE,但它沒有很好地工作。

我也嘗試找+ grep的方式

find . | grep -Fxv $(echo $(cat .rmignore) | tr ' ' "\n") 
+0

謝謝你們,所有這些答案。我需要一些時間閱讀,理解,測試和比較它們,然後才能接受它們。 –

+0

那麼,有沒有這些答案幫助你? –

回答

0

如果你有rsync,你也許可以到一個空目錄複製到目標之一,適合rsync的忽略文件。在-n之前先試試它,看看它會嘗試什麼,然後才能真正運行!如果我們假設在沒有將.rmignore文件包含換行符在他們的名字

1

這行做完美的工作

find . -type f | grep -vFf .rmignore 
+1

因此,您將爲每個正在考慮的文件運行一次'grep'?如果你有很多文件,這很重。 – ghoti

+1

如果.rmignore包含'1/*。php'作爲OP建議,這個grep將刪除所有包含'1/*。php'的文件,這意味着如果我們有一個像1/tutorialphp.txt一樣的文件,將會也被刪除。 Grep不適合大部分時間用於文件操作... –

+0

我沒有正確讀取謝謝。編輯 –

1

,以下就足夠:

# Gather our exclusions... 
mapfile -t excl < .rmignore 

# Reverse the array (put data in indexes) 
declare -A arr=() 
for file in "${excl[@]}"; do arr[$file]=1; done 

# Walk through files, deleting anything that's not in the associative array. 
shopt -s globstar 
for file in **; do 
    [ -n "${arr[$file]}" ] && continue 
    echo rm -fv "$file" 
done 

注:未經測試。 :-)另外,Bash 4引入了關聯數組。

另一種方法可能是用整個文件列表填充數組,然後刪除排除項。如果你正在處理數十萬個文件,這可能是不切實際的。

shopt -s globstar 
declare -A filelist=() 

# Build a list of all files... 
for file in **; do filelist[$file]=1; done 

# Remove files to be ignored. 
while read -r file; do unset filelist[$file]; done < .rmignore 

# Annd .. delete. 
echo rm -v "${!filelist[@]}" 

也未經測試。

警告:rm風險自負。可能含有堅果。保持備份。

我注意到這兩種解決方案都不能處理您的.rmignore文件中的通配符。爲此,您可能需要一些額外的處理...

shopt -s globstar 
declare -A filelist=() 

# Build a list... 
for file in **; do filelist[$file]=1; done 

# Remove PATTERNS... 
while read -r glob; do 
    for file in $glob; do 
    unset filelist[$file] 
    done 
done < .rmignore 

# And remove whatever's left. 
echo rm -v "${!filelist[@]}" 

而且......你猜對了。未經測試。這取決於$f作爲glob擴展。

最後,如果你想有一個重量級的解決方案,你可以使用findgrep

find . -type f -not -exec grep -q -f '{}' .rmignore \; -delete 

此運行grep爲每個文件正在考慮中。這不是一個bash解決方案,它只依賴於find,這很普遍。

請注意,如果您有包含換行符的文件,則所有這些解決方案都有發生錯誤的風險。

+2

downvote沒有評論? Niiice。 :) – ghoti

+0

好的解決方案。每個'rm'命令都可以用'echo rm'替換,以便在真正拍攝之前執行一種幹運行。 –

+0

@GeorgeVasiliou,謝謝,很好的建議。爲了清晰起見,我已經做出了改變,並且改進了變量名稱。 – ghoti

1

find的退出管道連接到另一個命令被認爲是不好的做法。您可以使用-exec,-execdir,然後使用命令,使用'{}'作爲文件的佔位符,使用​​3210來指示命令的結束。您還可以使用'+'將命令一起傳送到IIRC。

就你而言,你想列出目錄的所有競爭對手,並逐個刪除文件。

#!/usr/bin/env bash 

set -o nounset 
set -o errexit 
shopt -s nullglob # allows glob to expand to nothing if no match 
shopt -s globstar # process recursively current directory 

my:rm_all() { 
    local ignore_file=".rmignore" 
    local ignore_array=() 
    while read -r glob; # Generate files list 
    do 
     ignore_array+=(${glob}); 
    done < "${ignore_file}" 
    echo "${ignore_array[@]}" 

    for file in **; # iterate over all the content of the current directory 
    do 
     if [ -f "${file}" ]; # file exist and is file 
     then 
      local do_rmfile=true; 
      # Remove only if matches regex 
      for ignore in "${ignore_array[@]}"; # Iterate over files to keep 
      do 
       [[ "${file}" == "${ignore}" ]] && do_rmfile=false; #rm ${file}; 
      done 

      ${do_rmfile} && echo "Removing ${file}" 
     fi 
    done 
} 

my:rm_all; 
+0

很好地完成。除了我在循環內循環迭代的問題之外,我只有三個挑剔的建議。第一,OP的'.rmignore'文件包含目錄中的文件,因此您需要在最初的for循環中使用globstar。第二,'for'行末尾的';'是多餘的;也許這是一個分裂線想要評論的剩餘物? 3,bash有'true'和'false'作爲內建函數,所以如果你'local rmfile = true',後面設置'rmfile = false',你可以簡單的使用'$ rmfile && echo ...'。當然,這適用於更多描述性布爾變量,如'$ is_target'。 – ghoti

+0

我不知道'globstar',謝謝!我喜歡在我的「for」行結尾處打一個半列,這更像是一種風格的東西(除非這是一個問題)。你怎麼避免做一個雙循環? – jraynal

+0

呃..我的答案有幾個選項。在第一個版本中,使用'mapfile'來填充排除的數組,然後我們遍歷文件並忽略映射的文件。其他的解決方案填充一個數組,然後從中刪除東西,然後'rm'不管數組中剩下什麼。我希望這會更有效率,但嘿,你永遠不知道。 :) – ghoti

0

這是另一個bash的解決方案,它似乎在我的測試工作確定:

while read -r line;do 
exclude+=$(find . -type f -path "./$line")$'\n' 
done <.rmignore 

echo "ignored files:" 
printf '%s\n' "$exclude" 
echo "files to be deleted" 
echo rm $(LC_ALL=C sort <(find . -type f) <(printf '%s\n' "$exclude") |uniq -u) #intentionally non quoted to remove new lines 

Test it online here

+0

@СтаниславГольденшлюгер更新 - 沒有臨時文件 –

0

或者,你可能想看看最簡單的格式:

rm $(ls -1 | grep -v .rmignore) 
相關問題