删除除配置文件中提到的所有文件
我需要创建一个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)
谢谢你们,所有这些答案。我需要一些时间阅读,理解,测试和比较它们,然后才能接受它们。 –
那么,有没有这些答案帮助你? –