测试目录中是否存在与特定模式不匹配的任何文件

测试目录中是否存在与特定模式不匹配的任何文件

我在这方面遇到了很大困难。我正在尝试测试目录中是否存在与给定模式不匹配的文件,返回 true 或 false。

在这种情况下,$dir其中的文件不以下划线开头,_.我想我会尝试if [ -f $dir/!(_*) ]if [ $dir -f -name ! _* ]

或者 if ls $dir/!(_*) 1> /dev/null 2>&1

但它总是会说Too many arguments 或者syntax error near unexpected token "("

答案1

names=( "$dir"/[!_]* )

if [ -e "${names[0]}" ]; then
    echo 'there are filenames that do not start with underscore'
    printf '%d of them\n' "${#names[@]}"
fi

或者,对于/bin/shbash就此而言):

set -- "$dir"/[!_]*

if [ -e "$1" ]; then
    echo 'there are filenames that do not start with underscore'
    printf '%d of them\n' "$#"
fi

简而言之,扩展适当的通配模式并测试它是否与存在的内容匹配。

[!_]模式将匹配任何非下划线的字符。它类似于正则表达式 [^_],但文件名通配模式使用!而不是^否定字符类。

当模式匹配,默认情况下,它将保持未展开状态,因此这就是为什么我们使用测试-e来确保匹配列表中的第一项存在。我们无法真正测试返回列表的长度,因为如果长度为 1,它仍然可能没有匹配任何内容(除非您nullglob在 中设置了 shell 选项bash)。

如果你想测试的话,这会变得有点棘手常规文件具体来说,因为通配符模式匹配任何名称(目录、常规文件和所有其他类型的文件)。但这会做到这一点:

names=( "$dir"/[!_]* )

while [ "${#names[@]}" -gt 0 ] && [ ! -f "${names[0]}" ]; do
    names=( "${names[@]:1}" )
done

if [ -f "${names[0]}" ]; then
    echo 'there are at least one regular file here (or a symlink to one)'
    echo 'whose filename does not start with underscore'
fi

或者,对于/bin/sh

set -- "$dir"/[!_]*

while [ "$#" -gt 0 ] && [ ! -f "$1" ]; do
    shift
done

if [ -f "$1" ]; then
    echo 'there are at least one regular file here (or a symlink to one)'
    echo 'whose filename does not start with underscore'
fi

这种方法还可以检测到符号链接到名称不以下划线开头的常规文件。

循环对于移走我们可能匹配的非常规文件的任何文件名(例如目录名)是必要的。

在shell 中,您可以使用保证仅匹配常规文件的zsh模式(如果它匹配任何内容)。"$dir"/[^_]*(.)


对于更复杂的模式,您可以天真地将匹配的项目数与 匹配的项目数进行比较*。如果它们不同,则说明存在与复杂模式不匹配的名称。

在 中,您可以在使用 启用shell 选项后bash使用扩展的通配模式。一般形式为.您仍然需要像上面那样检查扩展的结果,看看它是否扩展为任何内容。!(PATTERN)extglobshopt -s extglob!(pattern1|pattern2|pattern3|etc)

名称不以下划线开头的示例可能会使用!(_*)扩展的通配模式,但请注意,这!(_)*不起作用,因为它会匹配每个可见名称,就像*会一样。

相关内容