受到该主题已接受答案的启发:列出目录中的 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}
如果您想处理空目录,那么您可能应该添加N
glob 限定符(就像设置了 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))'})