2012-10-15 47 views
3
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读入内存?

回答

3

否。glob在其中扩展的上下文基本上与普通的命令扩展上下文相同,其中处理所有扩展并以不可变的方式保存结果字以进行迭代。用for-in循环没有懒惰的迭代器。当然,扩张可能是副作用的,并且与球体混合在一起,所以他们必须进行热切的评估。这就是为什么find -exec [+;]仍然如此频繁地推荐使用globstar,因为它可能同时完成任务。

我不能说这个4096问题。我不认为这两者真的可比。 Shell for..in只是扩展单词并重复它们。

相关的常见问题是您是否可以执行诸如预读下一个要分配的值。据我所知,没有任何额外访问单词列表的Bourne-like shell。你必须为此使用数组。基本上所有for..in的限制都可以通过数组来克服。

这是我为Bash写的一个有趣的懒惰同步发电机。这是非常无用的。

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是写入时复制,并且仅在循环内使用shiftset时才复制位置参数。