列出匹配但具有任意扩展名的文件

列出匹配但具有任意扩展名的文件

给定一个带有路径的文件

/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%.*}".*

这里您唯一需要考虑的是引用变量替换的扩展。

您是否想在bashshell 中递归地执行此操作并包含隐藏名称,然后使用

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/filedir.d/对于dir.d/,因此上面首先剥离了尾随/s )。

所以要找到相同的文件,我们找到(递归地)附加了**/可选的文件。.<zero-or-more-non-dots>

find( -name, -path, ...)中的所有名称匹配谓词都-regex采用模式作为参数(shell 通配符或正则表达式),因此对于子字符串匹配,您需要转义其中的正则表达式运算符。或者您可以使用可以进行基本字符串比较的东西来后处理find -print0(或 POSIXly: )输出,例如使用 POSIX语法 和:find -exec printf '%s\0' {} +shperl

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,并且假设目录名不以换行符结尾,并且不以 / 开头-,也不是!/ ()...)

相关内容