从每个子文件夹中选取一个随机文件

从每个子文件夹中选取一个随机文件

受到该主题已接受答案的启发:列出目录中的 X 个随机文件,我尝试将两行放在一起,从当前目录的每个子文件夹中选取一个文件,但没有成功:

rand() REPLY=$RANDOM
for x in *; do y=$($x/*(o+rand[1])); print ${y:r}; done

看起来 的扩展y工作不正常。为什么?

上面的代码有什么问题?

答案1

问题不是 的展开y,而是分配给它的值。y

你有这个

y=$($x/*(o+rand[1]))

所分配的值是命令替换 ( ) 的结果$(…)。替换命令中唯一的“单词”是 glob 表达式。这意味着它将尝试将选定的文件作为命令运行(这可能会导致错误,并且不会产生有用的输出)。

为了让它工作(尽管语义发生了变化),您可以将命令替换分配更改为数组分配(即删除 后面$=):

y=($x/*(o+rand[1]))

如果要保留命令替换,则需要使其输出所选路径名,而不是尝试将其作为命令执行:

y=$(print -- $x/*(o+rand[1]))

但这里有一个隐藏的问题。每个 glob 将在单独的子 shell 中进行评估,因此每个目录将使用相同的随机数序列对其条目进行排序。如果您使用set -x并查看分配给每个目录的 REPLY 的值序列,您可以观察到这一点。这意味着不会跨目录独立选择文件。

您可以使用以下一些替代方法将全局扩展分配给参数(这些方法将计算保持$RANDOM在同一 shell 中,因此选择将(更)独立):

for y in $x/*(o+rand[1]); do print -- ${y:r}; done
set -- $x/*(o+rand[1]); print -l -- ${@:r}

如果您想处理空目录,那么您可能应该添加Nglob 限定符(就像设置了 NULL_GLOB 一样)。这也将有助于处理与外部 glob 匹配的非目录(尽管您可以使用/glob 限定符将该目录限制为仅目录)。

rand() REPLY=$RANDOM
for x in *(/); do y=($x/*(No+rand[1])); print -l -- ${y:r}; done

答案2

您可以向通配符添加修饰符:

for x (*(/)) print -rl -- $x/*(No+rand[1]:r)

也可以看看:

print -rl -- *(N/e{'REPLY=($REPLY/*(No+rand[1]:r))'})

相关内容