编辑二

编辑二

假设有以下命令

search /home/user proc .h .c .txt ...

我正在使用find命令构建一个脚本,以获取以给定名称开头并以给定扩展名之一结尾的所有文件。

我已经成功地使用循环构建了它:

directory=$1
fileName=$2
fileExtensions=""

for arg in "$@"
do
    #skipping first and second argument
     if [ $arg = $1 -o $arg = $2 ]; then 
        continue;
    fi
    fileExtensions+="${arg//.}\|"
done
#removing the last '|', otherwise regex parser error occurs
fileExtensions=${fileExtensions::-1}

find $directory -name "$fileName*" -regex ".*\.\($fileExtensions)"

有没有更优雅的方法使用正则表达式来实现这一点?

感谢您的帮助!

答案1

该脚本可以简化为:

directory=$1
fileName=$2
shift 2

a="$*"    b="${*#.}"
(( ${#a} - ${#b} - $# )) && echo "some extension(s) is(are) missing a leading dot." >&2

fileExtensions="$(IFS=\|; echo "${*#.}")"

find "$directory" -name "$fileName*" -regextype posix-egrep -regex ".*\.($fileExtensions)$"

默认情况下,find 接受 emacs 类型的正则表达式,要使用该类型需要几个反斜杠,例如\|.可以通过使用不同类型的正则表达式来避免这种情况(如上所述)。

其中${*#.}删除前导点(如果存在)并将所有剩余的“位置参数”与 IFS 的第一个字符的值连接起来,该值被设置|为用于子 shell 的执行。

只需要一个变量赋值和一个“参数扩展”,就足够了。


编辑
用于(( ${#a} - ${#b} - $# ))检查参数列表中提供的所有扩展名是否以点开头。

${#a}连接的所有参数的字符数 ( ) ( a=$*)
应等于连接的所有参数的
字符数 ( ${#b})(删除一个前导点) ( b=${*#.})
加上
参数的数量 ( $#)。

${#a} == ${#b} + $#

当且仅当所有参数都有一个前导点。

作为算术测试:

((  ${#a} - (${#b} + $#)  ))

或者也可以:

((  ${#a} - ${#b} - $#  )) && echo "missing leading dot(s)."

编辑二

命令行参数中给出的扩展列表在此处处理:

fileExtensions="$(IFS=\|; echo "${*#.}")"

从内到外,它的工作原理如下:

$*   # This generates a string of all positional arguments using IFS.

LESS=+'/Special Parameters' man bash

也就是说,“$*”相当于“$1c$2c...”,其中 c 是 IFS 变量值的第一个字符。

然后我们使用“参数扩展”来切割每个位置参数的前面:

${parameter#word}     # used as ${*# } above.

LESS=+/'parameter#word' man bash

如果参数是@或*,则依次对每个位置参数应用模式删除操作,并且扩展是结果列表。

word之前扩展中的 被设置为一个点,.从而删除了所有位置参数前面的点。

由于此时的 IFS 以一个|字符开头,因此该字符用于构建一个字符串,并|作为参数列表的分隔符,该参数列表在前面被去掉了一个点。

该字符串被提供给命令 echo 以使其打印。
但在执行 echo 命令之前,该变量$IFS被设置为|.

它被包装在命令执行中$(…)(创建一个子 shell,该子 shell 在结束时会忘记对 IFS 的更改)。

然后我们将字符串分配给一个变量:

fileExtensions="$(IFS=\|; echo "${*#.}")"

简而言之:转化.c .h .txtc|h|txt.

答案2

这似乎正则表达式应该可以工作,而我能想到的唯一其他选择是类似的东西,-name "proc*" \( -name "*.c" -o -name "*.h" ... \) 可能并没有那么简单。

一些可以做的小事:

1)您可以使用andshift来代替循环内的条件。$1$2

2)你可以结合-name-regex,这样最后你就得到了-regex ".*/proc.*\.(c|h|txt)"

3)如果想让代码更短,可以用IFSand连接位置参数$*

$ set .c .h
$ IFS='|'
$ echo "(${*/./})"
(c|h)

(我并不是说任何关于可读性或优雅的事情。)

相关内容