Bash for循环无法在OSX中的大型数据集上运行

时间:2013-07-26 20:35:53

标签: macos bash

我有一个包含大量子目录的目录,其中一些子目录中包含多个zip文件。我正在尝试编写一个bash脚本,它将遍历目录并查找名称“Archive-foo”进入子目录,如果它包含zip文件解压缩它们然后删除zip文件。

我编写的脚本在我的测试目录(5个子目录)上工作但是当我尝试在主存档目录(1200+子目录)上使用它时,它无法做任何事情。

for循环可以循环使用的最大项目数是多少?

这是我的代码

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
NUMBER=0
for i in $( ls )
do
#echo "$i"" is in the Top Level"
NUMBER=$[NUMBER+1]
if ($(test -d "$i")) 
then
    #echo "$i"" is a Directory"
    if [[ "$i" == *Archive* ]]
    then
        #echo "$i"" has Archive in the name"
        cd "$i" 
        unzip -n "*".zip
        mv *.zip ~/.Trash
    #else
        #echo "$i"" does not have Archive in the name"
    fi
 #else
    #echo "$i"" is NOT a Directory skipping"
fi
done
echo "$NUMBER of items"
IFS=$SAVEIFS

2 个答案:

答案 0 :(得分:3)

命令行的大小有限制,for i in $( ls )可能超出它。

请尝试使用以下语法:

ls | while read i;
do
  ...
done

唯一的问题是管道在子shell中运行while循环,因此NUMBER的赋值不会持久存储到原始shell进程中。你可以让循环在处理一行时打印一行,并将整个循环传递给wc -l以计算行数。

答案 1 :(得分:3)

Barmer回答了问题。使用for file in $(...)作为循环标题不是一个好主意:

  • 速度较慢:shell首先执行$(..)中的内容,然后运行for循环。在for完成之前,它无法启动$(...)
  • 它可以超出命令行缓冲区:shell执行$(..)然后将其放在命令行上。命令行缓冲区可能大约是32千字节,现在可能更多,但是如果你有10,000个文件而且每个文件平均有20个字符,那么最终会有超过200Kb的命令行缓冲区,
  • For循环在处理错误的文件名时非常糟糕:如果文件名中包含空格,则每个单词都被视为文件。

更好的结构是:

find . ... -print0 | while read -d $\0 file
do
   ...
done
  • 这可以在执行while read时执行find循环,使其更快。
  • 这不能超出命令行缓冲区。
  • 最重要的是,这个构造几乎可以处理任何类型的文件名。 find将返回由NUL字符分隔的每个文件 - 不能是文件名的字符。 -d $\0告诉read命令NUL字符是文件名之间的分隔符。这样可以处理文件名中的空格,制表符甚至新行

find也非常灵活。您可以将列表限制为仅限文件,特定年龄范围内的文件等。重放for循环所需的最常见的是:

$ find . -depth 1

就像ls -a

一样
$ find . \! -name ".*" -prune -a  -depth 1

行为与ls类似,并会跳过以.开头的文件名。