我需要創建一個bash腳本,它將刪除當前文件夾中除「文件」中提到的所有文件(「.rmignore」)以外的所有文件。刪除除配置文件中提到的所有文件
該文件可能包含與當前文件夾相關的地址,該文件夾也可能包含星號(*)。
例如:
1.php
2/1.php
1/*.php
我已經在努力與GLOBIGNORE,但它沒有很好地工作。
我也嘗試找+ grep的方式
find . | grep -Fxv $(echo $(cat .rmignore) | tr ' ' "\n")
我需要創建一個bash腳本,它將刪除當前文件夾中除「文件」中提到的所有文件(「.rmignore」)以外的所有文件。刪除除配置文件中提到的所有文件
該文件可能包含與當前文件夾相關的地址,該文件夾也可能包含星號(*)。
例如:
1.php
2/1.php
1/*.php
我已經在努力與GLOBIGNORE,但它沒有很好地工作。
我也嘗試找+ grep的方式
find . | grep -Fxv $(echo $(cat .rmignore) | tr ' ' "\n")
如果你有rsync
,你也許可以到一個空目錄複製到目標之一,適合rsync的忽略文件。在-n
之前先試試它,看看它會嘗試什麼,然後才能真正運行!如果我們假設在沒有將.rmignore
文件包含換行符在他們的名字
這行做完美的工作
find . -type f | grep -vFf .rmignore
因此,您將爲每個正在考慮的文件運行一次'grep'?如果你有很多文件,這很重。 – ghoti
如果.rmignore包含'1/*。php'作爲OP建議,這個grep將刪除所有包含'1/*。php'的文件,這意味着如果我們有一個像1/tutorialphp.txt一樣的文件,將會也被刪除。 Grep不適合大部分時間用於文件操作... –
我沒有正確讀取謝謝。編輯 –
,以下就足夠:
# 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擴展。
最後,如果你想有一個重量級的解決方案,你可以使用find
和grep
:
find . -type f -not -exec grep -q -f '{}' .rmignore \; -delete
此運行grep
爲每個文件正在考慮中。這不是一個bash解決方案,它只依賴於find
,這很普遍。
請注意,如果您有包含換行符的文件,則所有這些解決方案都有發生錯誤的風險。
將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;
很好地完成。除了我在循環內循環迭代的問題之外,我只有三個挑剔的建議。第一,OP的'.rmignore'文件包含目錄中的文件,因此您需要在最初的for循環中使用globstar。第二,'for'行末尾的';'是多餘的;也許這是一個分裂線想要評論的剩餘物? 3,bash有'true'和'false'作爲內建函數,所以如果你'local rmfile = true',後面設置'rmfile = false',你可以簡單的使用'$ rmfile && echo ...'。當然,這適用於更多描述性布爾變量,如'$ is_target'。 – ghoti
我不知道'globstar',謝謝!我喜歡在我的「for」行結尾處打一個半列,這更像是一種風格的東西(除非這是一個問題)。你怎麼避免做一個雙循環? – jraynal
呃..我的答案有幾個選項。在第一個版本中,使用'mapfile'來填充排除的數組,然後我們遍歷文件並忽略映射的文件。其他的解決方案填充一個數組,然後從中刪除東西,然後'rm'不管數組中剩下什麼。我希望這會更有效率,但嘿,你永遠不知道。 :) – ghoti
這是另一個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
@СтаниславГольденшлюгер更新 - 沒有臨時文件 –
或者,你可能想看看最簡單的格式:
rm $(ls -1 | grep -v .rmignore)
謝謝你們,所有這些答案。我需要一些時間閱讀,理解,測試和比較它們,然後才能接受它們。 –
那麼,有沒有這些答案幫助你? –