将“find -regextype egrep”设为别名

将“find -regextype egrep”设为别名

我刚刚开始学习正则表达式,并想在任何地方使用它而不是其他正则表达式进行练习。

我在尝试查找带有扩展名的文件时遇到这种情况sh or md

$ find . regex ".*\.(sh|md)$"
.
./bogus.py
./cofollow.py
./data8.txt
./example.sh
./longest_word_2.sh
./posit_param.sh
./cobroadcast2.py

不幸的是它输出/bogus.py

我注意到 BRE 规则并尝试逃脱()

$ find . -regex ".*\.\(sh|md\)$"
#get nothing return

经过一系列搜索,我得到了 -regextype 解决方案正则表达式 - 查找文件

$ find . -regextype posix-extended -iregex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh

$ find . -regextype egrep -iregex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
./table_regex_bat.md

此外,一个很好的模块化解决方案

$ find -type f | egrep ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
./table_regex_bat.md

然而,BSD 中有一个快捷方式可以使用谓词来完成此类任务-E

$ /usr/bin/find -E . -regex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh

我决心只使用 GNU 工具,以使我的代码和技能具有可移植性。

所以我开始别名“find -regextype egrep”,
不幸的是find获取了$1作为路径。

我怎样才能方便地解决他们的问题?

答案1

不要使用 analias来传递参数。它们不可移植并且仅在交互式 shell 上有用。使用函数代替并传递参数作为所需的路径

regexFind() {
    (( "$#" )) || { printf 'Insufficient arguments provided \n' >&2; return 1; }
     find "$1" -regextype egrep -iregex ".*\.(sh|md)$"
}

并将函数调用为

regexFind "/home/foo/bar"

另外,为了补充您的发现,请注意,它bash还有一种内在的 glob 文件方法。您只需要启用几个扩展 shell 选项即可使其工作。启用-s该选项并-u禁用它。

允许nullglob忽略未扩展的全局结果作为有效匹配。因此,假设您想匹配以*.shand结尾的文件*.md,您只需导航到该特定目录并执行

shopt -s nullglob
fileList=(*.sh)
fileList+=(*.md)
shopt -u nullglob

并打印结果如下所示。请记住引用扩展名以防止文件名进行分词。

printf '%s\n' "${fileList[@]}"

答案2

请注意, GNUfind的默认正则表达式不是 BRE,而是来自某些古老版本的 GNU 的正则表达式emacs(BRE 和 ERE 之间的某种混合体,例如,+受支持,但您需要\(...\)并且|受支持,但作为\|)。

对于 BSD find,默认值为 BRE,您可以使用该-E选项来启用 ERE,因此,只需执行以下操作:

alias efind='find -E'

或者:

efind() { find -E "$@"; }

在 GNU 中find,启用 ERE 是通过-regextype posix-extended谓词而不是选项来实现的。该谓词必须出现在文件名之后,如果存在,则必须出现在选项之后和使用它们的-regex或之前。-iregex

GNUfind语法是:

find [options] [files] [predicates]
                      ^

所以你需要将其插入那里(标有 的位置^)。

因此,在定义包装函数或脚本时,您需要考虑到这一点:跳过所有选项和文件名并在-regextype posix-extended它们后面插入。

efind() (
  found_predicate=false
  for arg do
    "$found_predicate" || case $arg in
      (-[LPDd]|-[OD]*) ;;  # skip options
      (-*|['()!'])
        set -- "$@" -regextype posix-extended
        found_predicate=true;;
    esac
    set -- "$@" "$arg"
    shift
  done
  
  exec find "$@"
)

其他一些注意事项:

  • 您打印的第一个bogus.py不是因为使用了 BRE,而是因为您使用regex-regex.regex被视为文件名,而不是谓词。
  • find . | egrep ...无效,因为文件路径可能由多行组成。使用 GNU 工具或兼容工具,您可以find . -print0 | grep -zE ...处理 NUL 分隔的记录(tr '\0' '\n'如果用于显示,则可以通过管道传输到)。

答案3

find . -type f \( -name '*.sh' -o -name '*.md' \)

这适用于 的所有实现,find因为它不需要支持正则表达式匹配。

为了使其更加灵活:

suffixfind () (
    dir=$1
    shift

    for suf do
        set -- "$@" -o -name "*.$suf"
        shift
    done
    shift

    find "$dir" -type f \( "$@" \)
)

这个辅助 shell 函数(可以在任何sh类似的 shell 中工作)将挑选出第一个命令行参数并将其放入变量中dir。然后,它会-name "*.<suf1>" -o -name "*.<suf2>" (etc.)在函数的命令行上构造一个包含所有文件名后缀的列表,然后调用find该列表以查找 中或下的文件$dir

你会像这样使用它

suffixfind /usr sh md txt

查找名称以.sh.md.txt路径中或路径下结尾的所有常规文件/usr

bash使用数组和bash局部变量对上述内容进行更详细的变体:

suffixfind () {
    local dir=$1
    shift

    local names

    names=( -name "*.$1" )
    shift
    for suf do
        names+=( -o -name "*.$suf" )
    done

    find "$dir" -type f \( "${names[@]}" \)
}

关于您提到的 GNU 工具和可移植性:请注意,非 Linux 系统上的 GNU 工具有时可用,但g工具名称带有前缀。因此, GNUfind将可用以将其与系统上的gfind本机实现区分开来。find

因此,您的“GNU 可移植”方法必须在测试是否实际上是 GNUgfind之前测试是否可用。直到您完成此操作(可能通过测试返回状态和输出),您才能放心地知道您正在处理 GNU 。findfindfind --versionfind

相关内容