set +f; rm *; touch a; for i in *; do touch b; echo $i; done
在我尝试的所有shell中(破折号,ksh,zsh,bash),上面的代码段只输出“a”。在C中实现相同的内容(readdir上的opendir / loop创建文件)也只输出“a”。但是,如果目录包含足够的文件(~4096),则C实现通常也会输出“b”。 (即,readdir返回在opendir之后创建的文件的结果)。我在shell标准中没有看到任何内容,表明在这种情况下shell应该如何响应。一个标准的兼容shell可以进入在glob之后创建的文件的循环吗?这将是一个非常理想的特性,因为它意味着shell在执行任何操作之前不会将整个glob读入内存。在预期目录包含许多文件的情况下,将glob读入内存通常需要几秒钟,这是浪费时间。
是否有任何shell实现在进入循环之前没有将整个glob读入内存?
答案 0 :(得分:3)
没有。 glob正在扩展的上下文基本上与普通命令扩展上下文相同,其中所有扩展都被处理并且所得到的单词以不可变的方式保存以进行迭代。 for-in循环没有延迟迭代器。当然,扩展可能是有效的,并且与globs混合在一起,因此必须对它们进行热切评估。这就是为什么find -exec [+;]
在可以同时执行的情况下仍然比globstar更频繁推荐的原因。
我对这个4096问题一无所知。我不认为这两者真的可比。 Shell for..in只是扩展单词并迭代它们。
相关常见问题解答是您是否可以执行预先指定要分配的下一个值的操作。据我所知,没有类似bourne的shell可以提供对单词列表的任何额外访问权限。你必须使用数组。基本上for..in
的所有限制都可以通过数组来克服。
这是我为Bash写的一个有趣的懒人coproc生成器。这很没用。
coproc x { while :; do find . -type f -maxdepth 1 -exec sh -c 'read; echo "$1"' -- {} \;; done; };
while :; do
echo 1 >&"${x[1]}"
read -ru "${x[0]}" file
echo "$file"
sleep 1
done
还有一个for..in
小问题与问题没有关系 - 在ksh93和Bash的git devel分支中,有可能以一种有趣的方式使用“控制变量”
function f {
nameref x # Chet may decide not to emulate the typeset -n aliases
for x; do
x=hi
done
}
typeset -a arr
f 'arr['{0..3}']'
typeset -p arr # arr=(hi hi hi hi)
每次迭代都会将给定对象的引用分配给x。当然在ksh中可以是任意复杂的数据类型。我想这可能会被滥用以某种方式模拟懒惰。不幸的是,这种模式似乎不适用于mksh。
编辑忘了自写这篇文章以来我发现很多shell确实优化了for x
语法。我假设至少for x in
是写时复制,只有在循环中使用shift
或set
时才会复制位置参数。