Answers:
find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort |uniq
上面的代码查找当前目录(.
)下的所有文件,这些文件是常规文件(-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
并且,对于MacOS sed:
find . -type f -name '*f*' | sed -E 's|/[^/]+$||' |sort -u
另外,如果您的find
命令支持,则可以find
直接打印目录名称。这避免了以下需求sed
:
find . -type f -name '*f*' -printf '%h\n' | sort -u
上面的版本会被包含换行符的文件名混淆。一个更强大的解决方案是对NUL终止的字符串进行排序:
find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'
-E
为MacOS 显示。
基本上可以使用两种方法来执行此操作。一个将解析字符串,而另一个将对每个文件进行操作。解析字符串中使用的工具,例如grep
,sed
或awk
明显要快一些,但这里有两个展示一个例子,以及如何可以“曲线”的2种方法。
对于以下示例,我们将使用以下数据
$ 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}
*f*
从中删除一些文件dir1/*
:
$ rm dir1/dir10{0..2}/*f*
在这里,我们要使用以下工具,find
,grep
,和sort
。
$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/
与以前相同的工具链,除了这次我们将使用dirname
而不是grep
。
$ find . -type f -name '*f*' -exec dirname {} \; | sort -u | head -5
./dir1/dir103
./dir1/dir104
./dir1/dir105
./dir1/dir106
./dir1/dir107
注意:以上示例head -5
仅用于限制我们为这些示例处理的输出量。通常会将其删除,以获取完整的列表!
我们可以用来time
看看这两种方法。
目录名
real 0m0.372s
user 0m0.028s
sys 0m0.106s
grep
real 0m0.012s
user 0m0.009s
sys 0m0.007s
因此,如果可能的话,最好总是处理字符串。
grep和PCRE
$ find . -type f -name '*f*' | grep -oP '^.*(?=/)' | sort -u
sed
$ find . -type f -name '*f*' | sed 's#/[^/]*$##' | sort -u
awk
$ find . -type f -name '*f*' | awk -F'/[^/]*$' '{print $1}' | sort -u
该答案基于slm答案是无耻的。这是一种有趣的方法,但是如果文件和/或目录名称具有特殊字符(空格,半列...),则存在局限性。一个好习惯是使用find /somewhere -print0 | xargs -0 someprogam
。
对于以下示例,我们将使用以下数据
mkdir -p dir{1..3}/dir\ {100..112}
touch dir{1..3}/dir\ {100..112}/nile{1..5}
touch dir{1..3}/dir\ {100..112}/file{1..5}
touch dir{1..3}/dir\ {100..112}/kni\ fe{1..5}
*f*
从中删除一些文件dir1/*/
:
rm dir1/dir\ 10{0..2}/*f*
$ find -type f -name '*f*' -print0 | sed -e 's#/[^/]*\x00#\x00#g' | sort -zu | xargs -0 -n1 echo | head -n5
./dir1/dir 103
./dir1/dir 104
./dir1/dir 105
./dir1/dir 106
./dir1/dir 107
注意:以上示例head -5
仅用于限制我们为这些示例处理的输出量。通常会将其删除,以获取完整的列表!另外,替换echo
您要使用的命令。
uniq
到混合通过删除重复的线路是已经紧挨着彼此有很大帮助。find . -type f -name '*f*' -printf '%h\0' | uniq -z | sort -zu | tr '\0' '\n'
。或者,如果您的工具较旧,则uniq可能没有-z选项。find . -type f -name '*f*' -printf '%h\n' | uniq | sort -u