2017-03-16 110 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) 
相关问题