![列出匹配但具有任意扩展名的文件](https://linux22.com/image/196858/%E5%88%97%E5%87%BA%E5%8C%B9%E9%85%8D%E4%BD%86%E5%85%B7%E6%9C%89%E4%BB%BB%E6%84%8F%E6%89%A9%E5%B1%95%E5%90%8D%E7%9A%84%E6%96%87%E4%BB%B6.png)
给定一个带有路径的文件
/foo/bar/a.b.c.d.x
我希望能够找到与 /foo/bar/abcd* 匹配且具有任何最终扩展名的所有文件
这是我迄今为止一直在使用的
findSiblingFiles () {
local filePath="$1"
find "$(dirname "$filePath")" -name "$(basename "${filePath%.*}").*"
}
这在大多数情况下都有效,但如果文件包含方括号(例如“abc[helloworld].x”),则会中断。如果我反斜杠转义方括号,我可以让它工作,但要在函数中做到这一点,我必须使用正则表达式添加反斜杠,这会进入混乱的领域,我想知道我是否缺少一种更简单的方法做这个。
理想情况下它应该找到带有以下内容的文件任何文件名中的字符(方括号、大括号、括号等),并且时间效率是一个考虑因素。
答案1
要查看与该模式匹配的所有文件/foo/bar/a.b.c.d.*
,请使用
printf '%s\n' /foo/bar/a.b.c.d.*
要查看与模式匹配的所有文件/foo/bar/a.b.c.[helloworld].*
,其中[hellowerld]
是文字,请使用
printf '%s\n' /foo/bar/a.b.c.'[helloworld]'.*
即,引用模式中需要按字面意思表达的部分。
如果您获得该字符串a.b.c.[helloworld].x
,并且希望查看/foo/bar
该字符串下的所有文件,该文件与删除最后一个.x
并将其替换为时获得的模式相匹配.*
,请使用
string='a.b.c.[helloworld].x'
printf '%s\n' /foo/bar/"${string%.*}".*
这里您唯一需要考虑的是引用变量替换的扩展。
您是否想在bash
shell 中递归地执行此操作并包含隐藏名称,然后使用
shopt -s globstar dotglob
string='a.b.c.[helloworld].x'
printf '%s\n' /foo/bar/**/"${string%.*}".*
您的函数可能会被编写为(无需在子目录中搜索)
findSiblingFiles () {
printf '%s\n' "${1%.*}".*
}
或者,如果模式不匹配,让它不输出任何内容,甚至不输出空行,
findSiblingFiles () (
shopt -s nullglob
set -- "${1%.*}".*
[ "$#" -gt 0 ] && printf '%s\n' "$@"
)
请注意,由于我们只修改第一个参数的“tail”,因此不需要将文件名与目录路径分开,除非我们想验证给定文件名中是否确实有一个点,下面的代码就是这样做的。
findSiblingFiles () (
shopt -s nullglob
if [[ ${1##*/} != *.* ]]; then
echo 'Filename has no suffix' >&2
return 1
fi
set -- "${1%.*}".*
[ "$#" -gt 0 ] && printf '%s\n' "$@"
)
测试:
$ findSiblingFiles ~myself/local/src/project/doc/document.txt
/home/myself/local/src/project/doc/document.man
/home/myself/local/src/project/doc/document.mdoc
/home/myself/local/src/project/doc/document.txt
答案2
和zsh
:
filesWithSameRootName() {
set -o localoptions -o extendedglob
local filepath=${1%%/#}
print -rC1 -- $filepath:h/**/$filepath:r(|[^.]#)(ND)
}
$filepath:r
就像在 csh 或 vim 中一样(或者bash
但仅用于历史扩展)返回根路径的,即去掉扩展名的文件路径。对于dir.d/file.foo.bar
,那就是,dir.d/file.foo
对于dir.d/file
,那就是dir.d/file
(dir.d/
对于dir.d/
,因此上面首先剥离了尾随/
s )。
所以要找到相同的文件根,我们找到(递归地)附加了**/
可选的文件。.<zero-or-more-non-dots>
find
( -name
, -path
, ...)中的所有名称匹配谓词都-regex
采用模式作为参数(shell 通配符或正则表达式),因此对于子字符串匹配,您需要转义其中的正则表达式运算符。或者您可以使用可以进行基本字符串比较的东西来后处理find -print0
(或 POSIXly: )输出,例如使用 POSIX语法 和:find -exec printf '%s\0' {} +
sh
perl
filesWithSameRootName() {
find "$(dirname -- "$1")" -exec printf '%s\0' {} + |
FILE="$1" perl -l -0ne '
BEGIN{
$root = $ENV{FILE};
$root =~ s{.*/}{};
$root =~ s{\.[^./]*\Z}{};
}
print if m{/\Q$root\E(\.[^.]*)?\Z}'
}
(尽管没有由 glob 进行排序zsh
,并且假设目录名不以换行符结尾,并且不以 / 开头-
,也不是!
/ (
,)
...)