我需要用换行符加入文件名生成模式的扩展,如下所示:
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
请注意,仅当您全局设置了该选项时才需要这些[^.]
或。^D
dotglob
print -C1
print -l
在一列上打印,它与空参数列表的情况相同(这里可能会发生这种情况,因为我使用N
ullglob 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