查找/检测文件名组

查找/检测文件名组

在 fs 上我有这样的文件: PREFIX_GROUPNAME_OTHERNAMES[.txt|.*]

例如:

A_ABC_A.txt
A_ABC_B.txt
A_ABC_C.txt
A_XYZ_A.txt
A_XYZ_B.txt
A_XYZ_C.txt

对于一些进一步的任务,我想获取组名称。

$# command i'm looking for
result:
> ABC XYZ

我知道名称结构而不是组名称。

想法(但似乎非常昂贵!(在大型列表中):

  • 扫描所有文件
  • 拆分名称,按组名称创建列表
  • 返回组

find 和 awk 也许 tr 似乎是我在寻找解决方案时所寻找的

编辑:

这给出了一个不唯一的列表:

find ./ -iname '*.txt' | xargs -n 1 | cut -d '_' -f 2
> ABC
> ABC
> ABC
> XYZ
> XYZ
> XYZ

答案1

下面将仅使用shell字符串操作和标准工具sort,以避免解析输出ls或者find,强烈建议不要这样做:

for f in *.*; do gr=${f#*_};gr=${gr%_*}; printf "%s\n" "$gr"; done | sort -u

在你的情况下,它应该准确输出

ABC
XYZ

解释:

  • 我们迭代所有匹配的文件名*.*(应该是“最小综合”模式来捕获您所说的所有文件名)
  • 通过 shell 字符串操作,我们首先删除第一个之前的所有内容_,然后在第二步中删除从最后一个开始的所有内容_
  • 我们通过以下方式输出结果printf(正如 Stéphane Chazelas 所指出的,您的 shell 不太可能缺少该命令)

最终的输出还不是唯一的。为了删除重复项,我们通过管道输出sort -u

笔记如果 - 正如您所说 - 您有很多与此模式匹配的文件,则您的for循环参数列表可能会超出 shell 的内部限制。此外,虽然此方法避免了与文件名中的特殊字符相关的许多陷阱,但使用printfsort意味着如果文件名包含换行符(这是许多文件系统上文件名的有效字符),该方法将会失败。

答案2

zsh

typeset -U groups=( **/*_*_*.*(Ne['REPLY=${${(s[_])REPLY:t}[2]}']) )
  • typeset -U groups=(...):定义为具有唯一成员的groups数组U
  • **/*_*_*.*: 文件名在最右边、当前工作目录或下面至少有一个.和至少两个s_.
  • (Ne['code']): glob 限定符进一步限定 glob
  • N: Nullglob: 如果没有匹配则展开为空
  • e['code']变换每个 glob 扩展1(在$REPLYcode
  • $REPLY:tt文件的 ail(基本名称)。
  • ${(s[_])var}: 分裂_(然后我们用 进行第二个[2])。

使用bash(GNU shell)、GNUfind和 GNU awk,您可以执行类似的操作:

readarray -td '' groups < <(
  LC_ALL=C find . -name '.?*' -prune -o \
    -name '*_*_*.*' -printf '%f\0' |
    gawk -v RS='\0' -v ORS='\0' -F _ '!seen[$2]++ {print $2}'
)

这些没有假设在前两个字符之间可以找到什么字符或非字符_

两者都会跳过隐藏文件和隐藏目录中的文件。要包含它们,请添加Dglob 限定符 inzsh或删除-name '.?*' -prune -oin find

如果有一个很大的文件列表,find基于 - 的文件将更加内存友好,因为它不会将整个列表存储在内存中。您可以采用类似的方法zsh

typeset -A seen=()
: **/*_*_*.*(Ne['! seen[${${(s[_])REPLY:t}[2]}]='])
groups=( ${(k)seen} )

¹ 该代码的退出状态也决定了该文件是否被选择,但这里代码始终返回 true

答案3

在得到答案的同时我也找到了一个解决方案。正如@AdminBee 也提到的:

find在来自文件系统的巨大结果列表中,xargs如果您不能限制搜索模式(例如:“*.txt”),您可能会选择使用。

for f in ./some/path/*.txt; do gr=${f#*_};gr=${gr%_*}; echo "$gr"; done | sort -u
> ABC
> XYZ

find ./ -iname '*.txt' | xargs -n 1 | cut -d '_' -f 2 | sort -u
> ABC
> XYZ

相关内容