我的目录中有许多文件,我想删除除其中一个具有相同前缀的文件之外的所有文件。例如,我有带有模式的文件filename.__<random_string>.pdf
,(文件名可以是一定长度的任何字符串)
foo.__.pdf
foo.__resume.pdf
foo.__name.pdf
bar.__.pdf
bar.__resume.pdf
bar.__name.pdf
现在,我只想要其中具有相同前缀的三个文件中的一个,即,我只想要前三个文件中的一个和后三个文件中的一个。例如,目录应包含,
foo.__.pdf
bar.__.pdf
接受使用任何脚本语言或 shell 的回答。
答案1
#!/bin/bash
declare -A seen
for name in *.__*.pdf; do
prefix=${name%%.__*.pdf}
if [[ -z ${seen[$prefix]} ]]; then
printf 'keeping "%s"\n' "$name"
seen[$prefix]=1
else
printf 'deleting "%s"\n' "$name"
# rm -f -- "$name"
fi
done
*.__*.pdf
上面的脚本从当前目录中与文件名通配模式匹配的每个文件名中提取前缀。如果以前没有见过该前缀,则保留该文件。否则该文件将被删除(rm
为了安全起见,该命令当前已被注释掉)。
为了跟踪已看到的前缀,它们作为键存储在名为 的关联数组中seen
。关联数组是在bash
版本 4 中引入的。
由于*.__*.pdf
与相同前缀匹配的任何文件都是“等效的”,因此只需将所有这些文件重命名为相同的名称即可将它们减少为单个文件。
这不需要关联数组,可以通过以下方式轻松完成/bin/sh
:
#!/bin/sh
for name in *.__*.pdf; do
prefix=${name%%.__*.pdf}
printf 'moving "%s" to "%s.__.pdf"\n' "$name" "$prefix"
# mv -f -- "$name" "$prefix.__.pdf"
done
在这里,所有带有前缀的文件foo
都被移动到该名称foo.__.pdf
(mv
为了安全起见,该命令被注释掉)。
答案2
和zsh
:
all=(*.__*.pdf)
typeset -A hash
for f ($all) hash[${f%%.__*}]=$f
keep=($hash)
rm -f -- ${all:|keep}
在具有相同前缀的前缀中,将保留按词汇顺序排在最后的那个。您可以使用all=(*.__*.pdf(On))
相反 来反转该顺序all=(*.__*.pdf)
。
答案3
和zsh
:
declare -A h=()
rm -- *(e:'((h[${REPLY%%.*}]++))':)
不幸的是,这不允许设置顺序。在这里添加o
/ O
glob 限定符只会改变顺序后排除每个文件中的第一个,并e
评估是否按照找到文件的顺序包含/排除文件(即*(oN)
返回它们时无序)。
答案4
使用perl
我们可以打开当前目录上的目录句柄并读取名称与指定条件匹配的所有纯文件。具有相同前缀的第一个文件将其名称打印到标准输出,而具有该前缀的后续文件将被删除。前缀是哈希的键%h
perl -le '
opendir my $dh, "."
or die qq(Cannot opendir ".": $!);
-f && /^(.+?)\.__.*\.pdf$/ and
!$h{$1}++ ? print() : unlink()
while readdir $dh;
closedir $dh;
'
使用查找命令:
fx() {
set -- "$1" "$tmpdir/${1%%.__*.pdf}"
if [ ! -s "$2" ]; then
echo x > "$2"
shift "$#"
else
rm -f "$1"
fi
return "$#"
}
export -f fx
tmpdir="$(mktemp -d)" \
find . ! -name . -prune -type f \
-name '?*.__*.pdf' \
-exec sh -c 'fx "$1"' find-sh {} \; -print;
使用 python3 和 pathlib 模块,其想法与上面相同:
python3 -c 'from pathlib import Path
seen = {}
for f in Path(".").glob("?*.__*.pdf"):
if f.is_file():
p = f.name.find(".__")
k = f.name[0:p]
if p > 0:
if k in seen: f.unlink()
else:
print(f.name)
seen[k]=1
'