随着全局限定符可以zsh
通过多种方式对文件名通配模式匹配的结果进行排序。例如,该模式*(om)
将匹配当前目录中的所有非隐藏名称,并按修改时间戳排序。
然而,我有时想有一种方法随机化排序(例如,获取文件的随机样本)。据我所知,没有任何限定符可以直接执行此操作。
问题:如何从zsh
文件名通配模式中获取随机的路径名列表?
答案1
使用随机排序键 (全局限定符 oe
)::
*(Noe\''REPLY=$RANDOM,$RANDOM'\')
解释:
oe
后面跟着一个单字符分隔符、一段代码和另一个分隔符。代码块可能不包含分隔符。特殊字符需要转义,以便在解析全局限定符本身时不会解析它们。- 我使用
'
作为分隔符(使用反斜杠,因为它需要转义),并用 包裹代码以'
保护可能存在的特殊字符。这样我就可以编写任意代码,只要它不包含'
. - 依次针对每个匹配的文件名执行该代码块。
REPLY
最初设置为文件名,并且代码设置的任何内容都REPLY
用作排序键)。
要随机采样$n
元素,请添加[…]
限定符:
*(Noe\''REPLY=$RANDOM,$RANDOM'\'[1,$n])
有时,某些元素会获得相同的排序键,因此所有排列的可能性并不相同,稍微倾向于保留按目录顺序将排序函数应用于列表的任何结果,但偏差很小。我使用它$RANDOM,$RANDOM
作为排序键而不是$RANDOM
为了减少偏差:$RANDOM
是一个 15 位数字,当文件数量接近 2^15 时偏差会很明显。
请注意,$RANDOM
如果不担心轻微的偏差,那么这对于采样来说已经足够了。它不适合任何涉及安全的事情。如果您想要安全的随机排列,请使用 GNU coreutilsshuf
。 (如果您最喜欢的操作系统缺少本机shuf
并且出于某种原因您不想安装 GNU coreutils,您可以尝试伊原的重新实现反而。)
securely_permuted=("${(0)$(printf '%s\0' *(N) | shuf -z))}")
或更简单的版本可能会遇到命令行长度限制:
securely_permuted=("${(0)$(shuf -z -- *(N)))}")
1实验上,排序是稳定的(例如,*(omoe\''REPLY=1'\')
相当于*(om)
,但来自 的顺序*(oe\''REPLY=1'\')
与 不匹配*(oN)
。无论如何,它是有利于某些特定顺序的小偏差。