【问题标题】:Zsh glob: objects recursivley under sudirectories, excluding current/base directoryZsh glob:子目录下的对象递归,不包括当前/基本目录
【发布时间】:2019-12-08 01:29:16
【问题描述】:

我正在尝试在当前目录的所有子目录下递归地生成所有对象(文件、目录等)的文件名。排除当前目录中的对象。

换句话说,给定:

--dir1 --dir2.1
|      | dir2.2 --file3.1
|      --file2.1
--file1

我要生成:

./dir2.1
./dir2.2
./dir2.2/file3.1
./file2.1

我已经设置了 EXTENDED_GLOB 选项,并且我认为以下模式可以解决问题:

./**/*~./*

但它返回:

zsh: no matches found: ./**/*~./*

我不知道是什么问题,应该可以。

./**/* 给出:

./dir1
./dir2.1
./dir2.2
./dir2.2/file3.1
./file2.1
./file1

而 ./* 给出:

./dir1
./file1

./**/*~./* 怎么会失败?更重要的是,如何在不包括当前/基本目录中的元素的子目录中递归生成元素的名称?

谢谢。

【问题讨论】:

    标签: zsh glob


    【解决方案1】:

    (1)x~y glob 运算符使用 y 作为 shell 的常规模式匹配而不是文件名生成,因此 ./**/*~./* 给出“找不到匹配项”:

    % print -l ./**/*~./*
    ;# ./dir1   # <= './*' matches, so exclude this entry
    ;# ./dir2.1 # <= './*' matches, so exclude this entry
    ;# .. # ditto...
    ;# => finally, no matches found
    

    排除模式 ./* 匹配 glob ./**/* 生成的所有内容,因此 zsh 最终产生“未找到匹配项”。 (zsh 不会为~y 部分生成文件名。)

    我们可以使排除模式更精确/复杂一点,以排除当前目录中的元素。这样它以./ 开头,并且有一个或多个不是/ 的字符。

    % print -l ./**/*~./[^/]## ;# use '~./[^/]##' rather than '~./*'
    ./dir1/dir2.1
    ./dir1/dir2.2
    ./dir1/dir2.2/file3.1
    ./dir1/file2.1
    

    然后,为了去除当前目录组件 /dir1,我们可以使用 (2)estring glob 限定符,这样它会删除第一次出现的 /[^/]## (例如/dir1):

    # $p for avoiding repetitive use of the exclusion pattern.
    % p='./[^/]##'; print -l ./**/*~${~p}(e:'REPLY=${REPLY/${~p[2,-1]}}':)
    ./dir2.1
    ./dir2.2
    ./dir2.2/file3.1
    ./file2.1
    

    或者使用通常的数组/替换而不是 estring glob 限定符来剥离它:

    % p='./[^/]##'; a=(./**/*~${~p}) ; a=(${a/${~p[2,-1]}}); print -l $a
    ./dir2.1
    ./dir2.2
    ./dir2.2/file3.1
    ./file2.1
    

    最后,迭代当前目录的目录也可以完成这项工作:

    a=(); dir=;
    for dir in *(/); do
      pushd "$dir"
      a+=(./**/*)
      popd
    done
    print -l $a
    #=> ./dir2.1
        ./dir2.2
        ./dir2.2/file3.1
        ./file2.1
    

    这里有一些 zsh 文档。

    (1)x~y全局运算符:

    x~y

    (需要设置EXTENDED_GLOB。)匹配任何匹配模式x但不匹配y的东西。它的优先级低于除‘|’ 之外的任何运算符,因此‘*/*~foo/bar’ 将搜索‘.’ 中所有目录中的所有文件,然后排除‘foo/bar’ 如果有这样的匹配。 ‘foo~bar~baz’ 可以排除多个模式。在排除模式 (y) 中,‘/’‘.’ 没有像在通配符中通常那样进行特殊处理。

    --- zshexpn(1), x~y, Glob Operators

    (2)e字符串 全局限定符:

    e字符串
    +cmd

    ...
    在执行字符串期间,当前正在测试的文件名在参数REPLY 中可用;该参数可以更改为要插入到列表中的字符串,而不是原始文件名。

    --- zshexpn(1), estring, Glob Qualifiers

    【讨论】:

    • 非常感谢。这正是我一直在寻找的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2017-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-25
    • 2012-07-20
    相关资源
    最近更新 更多