从扩展文件名生成模式连接数组

从扩展文件名生成模式连接数组

我需要用换行符加入文件名生成模式的扩展,如下所示:

files=([^.]*(.U))
files=${(F)files}

或者基本上

files=$(find . -maxdepth 1 -type f -uid $EUID ! -name '.*' -exec basename '{}' \;)

我想知道如何使用 zsh 直接执行此操作。如何$files一次性扩展模式并将连接的数组分配为字符串?

我尝试了几十种变化,比如files=${(@F)${:-[^.]*(.U:wq)}}。我试过字符串glob 限定符,但files=([^.]*(.U^P:\\n:))插入一个单独的单词(两个空格,我也尝试更改 $IFS)并附加细绳在最后一项之后。如果有一个连接修饰符就好了。

我错过了什么吗?

答案1

${(F)...}是参数展开。尽管允许嵌套扩展,

如果使用${...}类型参数表达式或$(...)类型命令替换来代替姓名上面,它首先被扩展,并且结果被使用,就好像它是姓名

嵌套仅限于参数扩展和命令替换。([^.]*(.U))两者都不是 - 它是数组定义的一部分,由文件名生成组成。文件名生成不会在这种情况下发生 - 直到参数扩展完成后才会发生(嵌套命令替换除外,但这在这里并不是特别有用)。这在以下内容中得到了扩展参数扩展规则

在每个嵌套的替换级别,被替换的单词经历所有形式的单单词替换(即不是文件名生成),包括命令替换、算术扩展和文件名扩展(即前导~=)。

这就是为什么这两者不能一起使用。如果您只是想要一种内置方法来执行此操作,而不依赖于像 之类的外部工具find,命令替换可能仍然提供一种方法:

files=$(print -lr -- [^.]*(.U))

答案2

更多选项:

  • 使用匿名函数:

    (){ files=${(F)@}; } *(U.^D)
    
  • 使用printf -v

    printf -v files '%s\n' *(U.^D)
    

    (那个\n最后有一个)。$files不得事先声明为数组。

  • 你不能直接将全局扩展分配给标量当全局变量生成时,所有变量都在一次赋值中列表so 仅适合分配给数组或关联数组。如果你真的想要(但我不清楚为什么你会这样做),你可以通过使用命令替换来解决它,如 @Gilles 所示,尽管这意味着分叉一个子 shell 并删除所有尾随换行符。

    另一种不涉及分叉的方法是使用zsh's动态命名目录功能,这是调用自定义代码来生成自定义扩展的一种方法。

    zsh_directory_name_functions+=(expand-glob)
    expand-glob() {
      emulate -L zsh
      set -o extendedglob
      local match
      [[ $1 = n && $2 = (#b)glob:(*):(*) ]] && {
        local sep=$match[1]
        reply=(${~match[2]}(#qN))
        reply=(${(pj:$sep:)reply})
      }
    }
    

    然后执行以下操作:

    files=~[glob:$'\n':*(#qU.^D)]
    

但你确定这就是你想要的吗?只要你用/或以外的任何字符连接这些文件\0,你就无法恢复原始文件列表,因为 (/\0) 是文件名中唯一不能出现的字符。

为什么不存储在数组中:

files=(*(NU.^D))

然后你就知道你不会失去任何东西。

如果您需要将该列表打印给用户,您可以随时执行以下操作:

print -rC1 -- $files

或者使用支持的任何引用形式,zsh以免丢失信息:

print -rC1 -- "${(@q+)files}" # human friendly
print -rC1 -- "${(@qq)files}" # safe for reinput to the shell or z flag

请注意,仅当您全局设置了该选项时才需要这些[^.]或。^Ddotglob

print -C1print -l在一列上打印,它与空参数列表的情况相同(这里可能会发生这种情况,因为我使用Nullglob glob 限定符来避免没有匹配文件时出现错误),后者仍然输出空行。

如果您想将该文件列表存储到一个文件中,以便可以对其进行编辑,并稍后加载结果,同时仍然保留所有信息,您可以执行以下操作:

files=(*(.U^D))
printf '%q\n' $files > file

对于%q,文件名使用引号引起来zsh,因此例如名为 的文件foo<newline>bar将呈现为$'foo\nbar'.

然后你可以加载将编辑后的文件放入新数组中,如下所示:

newfiles=("${(f@Q)$(<file)}")

其中Q参数扩展标志撤消引用。

然后执行以下操作:

(($#files == $#newfiles)) &&
   for old new (${files:^newfiles}) mv -i -- $old $new

相关内容