用于删除文件名不在文本文件中的Shell脚本

时间:2017-06-18 22:14:21

标签: shell file scripting

我有一个txt文件,其中包含文件名列表

示例:

10.jpg
11.jpg
12.jpeg
...

在文件夹中,此文件应防止删除过程,其他文件应删除。

所以我想要这个问题的相反逻辑:Shell command/script to delete files whose names are in a text file

怎么做?

4 个答案:

答案 0 :(得分:3)

使用extglob和Bash扩展模式匹配!(pattern-list)

  

!(模式列表)
  匹配除了给定模式之外的任何内容   其中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

根据示例,已删除的文件为13.jpg 14.jpg 15.jpg 16.jpg a.txt

因此,使用extglob!(pattern-list),我们可以根据文件内容获取排除的文件。
此外,如果您要排除以.开头的条目,则可以使用dotglob启用shopt -s dotglob选项。

答案 1 :(得分:2)

这是一种适用于bash GLOBIGNORE的方法:

else if ($.inArray(colorchooser_value, valid_colors) == -1)
    // ...

很明显,Globing忽略了GLOBIGNORE bash变量中包含的任何内容(文件,模式等)。

这就是为什么最后$ 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 仅报告文件ls,因为文件10,11和12.jpg被忽略。

因此,使用13.jpg只会删除系统中的rm *.jpg

13.jpg

完成后,您只需将GLOBIGNORE设置为null:

$ rm -iv *.jpg
rm: remove regular empty file '13.jpg'? y
removed '13.jpg'

值得一提的是,在GLOBIGNORE中,您还可以应用glob模式而不是单个文件名,例如$ GLOBIGNORE= *.jpg

替代方案:
我们可以使用编程技术(grep,awk等)来比较ignorefile中的文件名和当前目录下的文件:

my*.mp3

注意:这也会使用bash进程替换,如果文件名包含新行,则会中断。

答案 2 :(得分:1)

George Vasiliou回答的另一个替代方法是使用Bash内置mapfile读取包含要保留的文件名的文件,然后检查要删除的每个文件是否在该列表中。

#! /bin/bash -eu

mapfile -t keepthose <keepme.txt
declare -a deletethose

for f in "$@"
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从它从文件中读取的行修剪尾随换行符。但是,不会修剪其他空白区域。如果您的文件名实际上包含空格,那么这可能是您想要的,但是如果有人在他们想要保留的重要文件的名称之前或之后意外地放置了一个空格,它也可能会产生微妙的意外。

请注意,我首先构建一个应删除的文件列表,然后一次删除它们,而不是单独删除每个文件。这样可以节省一些子进程调用。

如上所述,列表中的查找具有线性复杂度,这给出了整个脚本的二次复杂度(确切地说, N M 其中 N 是命令行参数的数量, M keepme.txt文件中的条目数。如果你只有几十个文件,这应该没问题。不幸的是,我不知道有更好的方法来检查Bash中的集合成员资格。 (我们不能将文件名用作关联数组中的键,因为它们可能不是正确的标识符。)如果您关心许多文件的性能,那么使用像Python这样更强大的语言可能是值得考虑的。

我还想提一下,上面的例子只是比较字符串。它不会意识到important.txt./important.txt是同一个文件,因此会删除该文件。在比较之前,使用readlink -f将文件名转换为规范路径会更加健壮。

此外,您的用户可能希望能够将全局模式(如important.*)放入要保留的文件列表中。如果您想要处理这些模式,则需要额外的逻辑。

总的来说,指定哪些文件删除似乎有点危险,因为错误是坏的。

答案 3 :(得分: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