从 glob 匹配返回随机项目

Kus*_*nda 4 zsh wildcards random

随着水珠预选赛中的zsh一个可以在文件名以各种方式通配符模式匹配的结果进行排序。例如,该模式*(om)将匹配当前目录中的所有非隐藏名称,按修改时间戳排序。

但是,我有时希望有一种随机排序的方法(例如,获取文件的随机抽样)。据我所知,没有限定符可以直接执行此操作。

问题:如何从zsh文件名通配模式中获取随机的路径名列表?

Gil*_*il' 5

使用随机排序键(全局限定符 oe)::

*(Noe\''REPLY=$RANDOM,$RANDOM'\')
Run Code Online (Sandbox Code Playgroud)

解释:

  • oe后跟一个字符分隔符、一段代码和另一个分隔符。代码块可能不包含分隔符。特殊字符需要进行转义,以便在解析 glob 限定符本身时不会解析它们。
  • 我使用'作为分隔符(使用反斜杠,因为它需要转义),并且我用代码包装'以保护可能存在的特殊字符。这样我就可以编写任意代码,只要它不包含'.
  • 依次为每个匹配的文件名执行这段代码。
  • REPLY最初设置为文件名,并且任何代码设置REPLY为用作排序键)。

$n随机采样元素,请添加[…]限定符:

*(Noe\''REPLY=$RANDOM,$RANDOM'\'[1,$n])
Run Code Online (Sandbox Code Playgroud)

有时,某些元素会获得相同的排序键,因此所有排列的可能性并不相同,稍微倾向于保留将排序函数应用于按目录顺序的列表¹ 的任何结果,但偏差很小。我$RANDOM,$RANDOM用作排序键而不是$RANDOM减少偏差:$RANDOM是一个 15 位数字,当文件数量接近 2^15 时,偏差会很明显。

请注意,$RANDOM如果轻微的偏差不是问题,这对于采样来说已经足够了。它不适合任何涉及安全的事情。如果您想要安全的随机排列,请使用 GNU coreutils 的shuf. (如果您最喜欢的操作系统缺少本机,shuf并且您出于某种原因不想安装 GNU coreutils,则可以尝试使用ibara 的重新实现。)

securely_permuted=("${(0)$(printf '%s\0' *(N) | shuf -z))}")
Run Code Online (Sandbox Code Playgroud)

或更简单的版本,可能会遇到命令行长度限制:

securely_permuted=("${(0)$(shuf -z -- *(N)))}")
Run Code Online (Sandbox Code Playgroud)

¹实验上,排序是稳定的(例如*(omoe\''REPLY=1'\'),相当于*(om),但顺序*(oe\''REPLY=1'\')不匹配*(oN)。无论如何,这是有利于某些特定顺序的小偏差。