获取包含名称包含字符串的文件的子目录列表

Muh*_*uhd 63 command-line find

如何获取包含名称与特定模式匹配的文件的子目录列表?

更具体地说,我正在寻找包含文件名中出现字母“f”的文件的目录。

理想情况下,该列表不会有重复项,并且只包含没有文件名的路径。

Joh*_*024 61

find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort |uniq
Run Code Online (Sandbox Code Playgroud)

以上查找当前目录 ( .)下的所有文件,这些文件是常规文件 ( -type f) 并f在其名称中的某处 ( -name '*f*')。接下来,sed删除文件名,只留下目录名。然后,对目录列表进行排序 ( sort) 并删除重复项 ( uniq)。

sed命令由一个替代品组成。它查找与正则表达式的/[^/]+$匹配项,并用空替换任何匹配项。美元符号表示该行的结尾。 [^/]+'表示一个或多个不是斜杠的字符。因此,/[^/]+$表示从最后一个斜杠到行尾的所有字符。换句话说,这与完整路径末尾的文件名匹配。因此, sed 命令删除文件名,保持文件所在目录的名称不变。

简化

许多现代sort命令都支持一个不必要的-u标志uniq。对于 GNU sed:

find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort -u
Run Code Online (Sandbox Code Playgroud)

而且,对于 MacOS sed:

find . -type f -name '*f*' | sed -E 's|/[^/]+$||' |sort -u
Run Code Online (Sandbox Code Playgroud)

此外,如果您的find命令支持它,则可以find直接打印目录名称。这避免了需要sed

find . -type f -name '*f*' -printf '%h\n' | sort -u
Run Code Online (Sandbox Code Playgroud)

更强大的版本(需要 GNU 工具)

上述版本会被包含换行符的文件名混淆。一个更健壮的解决方案是对以 NUL 结尾的字符串进行排序:

find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'
Run Code Online (Sandbox Code Playgroud)


Pat*_*lor 34

为什么不试试这个:

find / -name '*f*' -printf "%h\n" | sort -u
Run Code Online (Sandbox Code Playgroud)

  • @kkm 我同意这是最好的解决方案,但是 [`find` 的 POSIX 规范](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html) 实际上非常稀疏——`-printf` 运算符*未*指定。这不适用于 BSD `find`。所以,*不*“完全兼容 POSIX”。(尽管 `sort -u` [在 POSIX 中](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sort.html)。) (5认同)

slm*_*slm 8

基本上有两种方法可以用来做到这一点。一个将解析字符串,而另一个将操作每个文件。使用诸如grep, 之类的工具解析字符串sed,或者awk显然会更快,但这里有一个示例显示了两者,以及如何“分析”这两种方法。

样本数据

对于下面的示例,我们将使用以下数据

$ touch dir{1..3}/dir{100..112}/file{1..5}
$ touch dir{1..3}/dir{100..112}/nile{1..5}
$ touch dir{1..3}/dir{100..112}/knife{1..5}
Run Code Online (Sandbox Code Playgroud)

*f*dir1/*以下位置删除一些文件:

$ rm dir1/dir10{0..2}/*f*
Run Code Online (Sandbox Code Playgroud)

方法#1 - 通过字符串解析

在这里,我们将使用以下工具findgrep、 和sort

$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/
Run Code Online (Sandbox Code Playgroud)

方法#2 - 使用文件解析

与以前相同的工具链,除了这次我们将使用dirname代替grep

$ find . -type f -name '*f*' -exec dirname {} \; | sort -u | head -5
./dir1/dir103
./dir1/dir104
./dir1/dir105
./dir1/dir106
./dir1/dir107
Run Code Online (Sandbox Code Playgroud)

注意:以上示例head -5仅用于限制我们为这些示例处理的输出量。他们通常会被删除以获得您的完整列表!

比较结果

我们可以使用time来看看这两种方法。

目录名

real        0m0.372s
user        0m0.028s
sys         0m0.106s
Run Code Online (Sandbox Code Playgroud)

格雷普

real        0m0.012s
user        0m0.009s
sys         0m0.007s
Run Code Online (Sandbox Code Playgroud)

因此,如果可能,最好处理字符串。

替代字符串解析方法

grep & PCRE

$ find . -type f -name '*f*' | grep  -oP '^.*(?=/)' | sort -u
Run Code Online (Sandbox Code Playgroud)

sed

$ find . -type f -name '*f*' | sed 's#/[^/]*$##' | sort -u
Run Code Online (Sandbox Code Playgroud)

awk

$ find . -type f -name '*f*' | awk -F'/[^/]*$' '{print $1}' | sort -u
Run Code Online (Sandbox Code Playgroud)