删除目录中除与给定通配符模式匹配的文件之外的所有文件

删除目录中除与给定通配符模式匹配的文件之外的所有文件

我的目录中有许多文件,我想删除除其中一个具有相同前缀的文件之外的所有文件。例如,我有带有模式的文件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.__.pdfmv为了安全起见,该命令被注释掉)。

答案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/ Oglob 限定符只会改变顺序排除每个文件中的第一个,并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
'

相关内容