【问题标题】:Pattern-based filename filtering in gnu shell commandgnu shell 命令中基于模式的文件名过滤
【发布时间】:2016-12-07 15:36:11
【问题描述】:

假设我有一个包含这些文件的 active/ 目录

active/
foo.bar.abc
foo.bar.xyz
foo.bat.abc

archive/
foo.bat.xyz

我想编写一个命令以仅输出 active/ 中的唯一文件名(基于中间项的唯一性)并且与 archive/ 中已有的任何文件都不匹配(再次基于该中间项)。

示例输出:

foo.bar.abc

解释:输出foo.bar.abcfoo.bar.xyz 都没有关系。不是foo.bat.abc,因为foo.bat.xyz 存在于archive/

我找到了this 来帮助识别基于模式的唯一值,但我不知道如何将它与存档中不匹配的附加子句结合起来/

【问题讨论】:

  • 自己有什么尝试吗?
  • ls | awk -v re='foo\.[[:alpha:]]\.' 'match($0, re, a) && !(a[0] in p) {p[a[0]]; print}' 之类的东西会打印出单个目录的唯一文件名。我不确定从哪里开始将其与另一个目录的内容进行比较。
  • @JitterbugChew:所有文件都是word1.word2.word3类型的文件,你的要求是word2的唯一性吗?
  • @Inian 是的。 word1 是一致的,word2word3 不同

标签: regex bash shell unix awk


【解决方案1】:

这里其实不需要awk,你可以用简单的grep/sed和sort来做:

(ls ./archive | sed 's/^/1 /'; ls ./active | sed 's/^/2 /') | \
  sort --field-separator="." --key="2,2" --uniq --stable | \
  grep '^2 ' | sed 's/^2 //'

说明:

首先列出两个目录并标记哪些行来自哪个目录。然后将两个列表按中间部分排序。选项--field-separator="." 将所有行拆分为点上的字段,选项--key="2,2" 告诉按中间字段排序,即按点之间的部分。我们使用稳定的排序来确保存档中的行是第一个,并告诉 sort 只打印所有重复行的第一个匹配项。

最后,我们只过滤我们用2 标记的行,即来自./active 的行。

示例:

active/
  foo.aaa.xxx
  foo.bar.abc
  foo.bar.xyz
  foo.bat.abc
  zoo.aaa.xxx
  zoo.bbb.aaa


archive/
  aaa.bbb.zoo
  foo.bat.xyz

Result:
  foo.aaa.xxx
  foo.bar.abc

【讨论】:

  • 这很好用。我不会想到使用 sed 来标记每个目录的内容。我猜,例如,如果分隔符同时是 ;.,您可以在使用 sort 处理之前执行 sed 以使它们相同?
  • 是的,您可以在之前对输入进行预处理
【解决方案2】:

再次尝试使用GNU grepawkGNU findutils

$ grep -Fxvf <(find active/ -type f -printf '%P\n' | awk -F'.' '!seen[$2]++') <(find archive/ -type f -printf '%P\n' | awk -F'.' '!seen[$2]++')
foo.bar.xyz

我使用process-substitution &lt;() 运行find/awk 命令并将其传递给grep 以查找差异。

find 命令列出指定目录中的文件,每行一个条目,awk 通过保留不与2nd 单词重复的文件来过滤列表。对于awk,分隔符为. !seen[$2]++ 仅在之前未见过的情况下通过在数组中散列它来打印唯一行。

请记住find 中的-printf '%P POSIX 兼容的,并且可以与GNU findutils 一起使用。建议升级到它以使其正常工作。

其他可能的解决方案,逻辑类似,commjoinGNU coreutils 的两个部分如下:-

$ join -v 2 <(find active/ -type f -printf '%P\n' | awk -F'.' '!seen[$2]++') <(find archive/ -type f -printf '%P\n' | awk -F'.' '!seen[$2]++')
foo.bar.xyz

另一个comm

$ comm -13 <(find active/ -type f -printf '%P\n' | awk -F'.' '!seen[$2]++') <(find archive/ -type f -printf '%P\n' | awk -F'.' '!seen[$2]++')
foo.bar.xyz

【讨论】:

  • 也许我错误地复制粘贴了一些东西,但您的所有解决方案似乎都返回foo.bat.xyz,而不是foo.bar.xyz
  • @martin.macko.47:什么?他们都为我返回foo.bar.xyz。你能检查你正在使用的工具版本吗?他们需要GNU findutils 和其他人作为GNU coreutils 的一部分。我特别添加了一个注释,我的解决方案在这些工具下工作
  • $ find --version find (GNU findutils) 4.4.2 是不是版本不对?恕我直言,find 的输出没问题。你确定你的archive/ 中有foo.bat.xyz 而不是另一个吗?
  • @martin.macko.47:同意。您能否确保grep 和其他工具也相同,并在根级别调用命令,并在其下有activearchive 文件夹?
猜你喜欢
  • 2010-09-17
  • 2011-06-02
  • 1970-01-01
  • 1970-01-01
  • 2012-07-26
  • 1970-01-01
  • 2017-09-19
  • 1970-01-01
  • 2017-12-24
相关资源
最近更新 更多