Shell元字符 - 匹配这些类型文件名的较短方法?

时间:2011-02-14 22:50:59

标签: bash

对于练习,我写了一个由元字符组成的表达式,最多匹配3个大写字符。

实施例

a -> match
A -> match
Ab -> match
AbC -> match
AbCd -> match 
...
ABCD -> no match, 4 uppercase chars

这是我想出来的,但我觉得我可以缩短它

ls @(!(*[A-Z]*)|*[A-Z]*|*[A-Z]*[A-Z]*|*[A-Z]*[A-Z]*[A-Z]*)

修改

感到困惑。首先,我只允许使用元字符,没有正则表达式,没有测试,没有像awk / sed /其他的工具。 而且,大写字母不能是连续的。

修改

好的,这个似乎有用(但更长!)。

export LC_COLLATE=C

ls @(!(*[A-Z]*)|!(*[A-Z]*)[A-Z]!(*[A-Z]*)|[A-Z]!(*[A-Z]*)[A-Z]!(*[A-Z]*)|!(*[A-Z]*)[A-Z]!(*[A-Z]*)[A-Z]!(*[A-Z]*)[A-Z]!(*[A-Z]*)

5 个答案:

答案 0 :(得分:3)

你的模式对我不起作用。一个问题是,在许多非C语言环境中,[A-Z]包含一些小写字符。

$ for c in a A b B z Z; do if [[ $c = [A-Z] ]]; then echo "match: $c"; else echo "no match: $c"; fi; done
no match: a
match: A
match: b
match: B
match: z
match: Z

使用LANG=C再试一次。如果您想要匹配大写字符而不考虑区域设置,请使用[[:upper:]]

你的另一个原因是它的部分内容始终匹配。

例如:

!(*[A-Z]*)

(即使使用[[:upper:]]进行了更正)匹配(拒绝)任何仅包含大写字符的内容,无论长度如何。但是,(部分更正的)模式的其余部分显式包含大写字符,而由于星号而隐含地包含任何字符,包括大写字符。所以只是第一部分:

*[[:upper:]]*

表示要包含至少包含一个大写字符而不考虑的所有字符串,以及可能存在的字符数:一,十,百万。

相反,试试这个:

if [[ $string != *[[:upper:]]*[[:upper:]]*[[:upper:]]*[[:upper:]]* ]]
then
    echo "match: fewer than four uppercase character"
fi

它只是检查是否有四个或更多大写字符。

您还可以使用正则表达式(在Bash 3.2或更高版本中):

if [[ ! $string =~ ^.*[[:upper:]].*[[:upper:]].*[[:upper:]].*[[:upper:]].*$ ]]
then
    echo "match: fewer than four uppercase character"
fi

另一种方法是删除所有非大写字符并比较长度差异。

演示:

#!/bin/bash
strings[1]='AbCdEfghi'
strings[2]='ABCD'
strings[3]='Ab1Cd2Ef3ghi'
strings[4]='A1BbC2Dd'

for string in "${strings[@]}"
do
    test=${string//[^[:upper:]]}
    if (( ${#test} > 3 ))
    then
        echo "no match: $string"
    else
        echo "match: $string"
    fi
done

答案 1 :(得分:0)

尝试:

ls | grep -E '^([^A-Z]*[A-Z][^A-Z]*){0,3}$'

答案 2 :(得分:0)

$ echo "BCDAdf" | awk '{m=gsub(/[A-Z]/,"");print (m<4) ?"match":"no match"}'
no match

$ echo "CDAdf" | awk '{m=gsub(/[A-Z]/,"");print (m<4) ?"match":"no match"}'
match

答案 3 :(得分:0)

Erik提到使用grep,所以我也会使用它。

我认为应该是:

/bin/ls -1 | grep -E '^[^A-Z]*([A-Z][^A-Z]*([A-Z][^A-Z]*([A-Z][^A-Z]*)?)?)?$'

可以缩短为:

/bin/ls -1 | grep -E '^[^A-Z]*([A-Z][^A-Z]*){0,3}$'

如果您真的想使用bash扩展模式,它应该如下所示:

/bin/ls -1 *([^A-Z])?([A-Z]*([^A-Z]))?([A-Z]*([^A-Z]))?([A-Z]*([^A-Z]))

请注意,您必须设置LC_COLLATE=C才能生效。


如果你想把它写得更短,你可以使用参数扩展(即变量扩展)在文件名扩展之前发生的事实,并做一些奇怪的事情:

u='[A-Z]' # $u == uppercase characters
U='[^A-Z]' # $U == non-uppercase characters
/bin/ls -1 *($U)?($u*($U))?($u*($U))?($u*($U))

这是不是一个好主意,我留给你决定。 ; - )

答案 4 :(得分:0)

我相信这样的事情是正确的:

!(*([^[:upper:]])[[:upper:]]*([^[:upper:]])[[:upper:]]*([^[:upper:]])[[:upper:]]*([^[:upper:]])[[:upper:]]*)

当然还有extglob。