我有一个包含 1000 多个文件的目录。在一个文本文件中,我有大约 50 个文件名,每行一个。我想删除目录中文件名与列表中的条目不对应的所有文件。最好的方法是什么?我启动了一个 shell 脚本,但无法确定正确的命令来确定文件名是否在列表中。谢谢。
答案1
我意识到任何询问如何删除文件的问题都必须非常小心。我的第一个答案太仓促了,我没有考虑到文件列表可能格式错误而无法与egrep一起使用。我编辑了答案以降低这种风险。
这应该适用于名称中没有空格的文件:
首先重建文件列表以确保与确切的文件名匹配:
sed -e 's,^,^,' -e 's,$,$,' filelist > newfilelist
构建 rm 命令
cd your_directory
ls | egrep -vf newfilelist | xargs -n 1 echo rm > rmscript
检查 rm 脚本是否适合您(您可以使用“vim”或“less”来完成)。
然后执行操作:
sh -x rmscript
如果文件名称中包含空格(如果文件"
名称中包含 则这将不起作用):
ls | egrep -vf newfilelist | sed 's,^\(.*\)$,rm "\1",' > rmscript
当然文件列表不应该在同一目录中!
编辑:
Nathan 的文件列表包含与目录中所有文件匹配的名称(例如“html”与“bob.html”匹配)。所以没有删除任何内容,因为egrep -vf
吸收了所有的流。我添加了一个命令,在每个文件名周围放置“^”和“$”。我很幸运,内森的文件列表是正确的。如果是使用 CR-LF 结束行或附加空格的 DOS 格式,egrep 不会保留任何文件,并且所有文件都会被删除。
答案2
预先构造以下参数find
:
{
read -r
keep=( -name "$REPLY" ) # no `-o` before the first one.
while read -r; do
keep+=( -o -name "$REPLY" )
done
} < file_list.txt
find . -type f ! \( "${keep[@]}" \) -exec echo rm {} +
使用这些echo
零件来看看会构造出什么。拆下echo
部件即可实际运行。
更新: 演示:
##
# Demonstrate what files exist for testing.
# Show their whitespace:
~/foo $ printf '"%s"\n' *
" op"
" qr"
"abc"
"def"
"gh "
"ij "
"k l"
"keep"
"m n"
##
# Show the contents of the "keep" file,
# Including its whitespace:
~/foo $ cat -e keep
keep$
abc$
gh $
k l$
op$
##
# Execute the script:
~/foo $ { read -r; keep=( -name "$REPLY" ); while read -r ; do keep+=( -o -name "$REPLY" ); done } < keep
~/foo $ find . -type f ! \( "${keep[@]}" \) -exec rm {} +
##
# Show what files remain:
~/foo $ printf '"%s"\n' *
" op"
"abc"
"gh "
"k l"
"keep"
答案3
和zsh
:
mylist=(${(f)"$(<filelist)"})
print -rl -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)
filelist
它读取数组中的行,然后使用全局限定符/e
字符串仅全局/选择数组中不存在的文件名:.
仅选择常规文件(D
如果列表包含点文件则添加),而否定^e_'expression'_
进一步仅选择表达式返回 false 的文件,即如果它们的名称 ( $REPLY
)不是数组的元素。
如果您对结果满意,请替换print -rl
为rm
以实际删除文件:
rm -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)
要递归选择和删除文件,请使用*/**
glob${REPLY:t}
全局修饰符:
rm -- */**(.^e_'(($mylist[(Ie)${REPLY:t}]))'_)
答案4
运行以下脚本。
- 最初,我查找目录中存在的所有文件并将输出存储到另一个文件中
all_files
。 - 我们有一个文件,其中包含应该的文件列表不是删除(
not_to_be_deleted_files
)。 - 我将文件名添加到的末尾,
not_to_be_deleted_files
因为 我们需要这两个文件。files_to_be_deleted
not_to_be_deleted_files
- 现在,我正在使用 linux 命令查找需要删除的文件
join
并将输出重定向到files_to_be_deleted
文件。 - 现在,在最后一个 while 循环中,我正在读取其中的所有文件名
files_to_be_deleted
并删除该文件名中提到的文件。
脚本如下。
find /home/username/directory -type f | sed 's/.*\///' > all_files
echo all_files >> not_to_be_deleted_files
echo not_to_be_deleted_files >> not_to_be_deleted_files
echo files_to_be_deleted >> not_to_be_deleted_files
join -v 1 <(sort all_files_listed) <(sort files_not_to_be_deleted) > files_to_be_deleted
while read file
rm "$file"
done < files_to_be_deleted
聚苯乙烯:也许,如果您希望将其保存为脚本并运行它,您也可以使用 . 添加脚本名称echo scriptname >> not_to_be_deleted_files
。
虽然这不是必需的,但我更愿意这样做,因为以后就不会后悔了。我测试了一小组文件,它在我的系统中工作。但是,如果您想确定,请先在一个test
目录中尝试,然后删除原始目录中的文件。