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



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

我编写的脚本适用于我的测试目录(5个子目录),但当我尝试在主归档目录(1200多个子目录)上使用它时,它什么都做不了。

for循环可以循环通过的项目的最大数量是多少?

这是我的代码

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "nb")
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

命令行的大小有限制,for i in $( ls )可能会超过这个限制。

请尝试以下语法:

ls | while read i;
do
  ...
done

唯一的问题是管道在子shell中运行while循环,因此对NUMBER的分配不会持久存在于原始shell进程中。您可以让循环在处理一行时打印一行,并通过管道将整个循环发送到wc -l来计算行数。

Barmer的回答切中要害。使用for file in $(...)作为循环头不是一个好主意:

  • 它更慢:shell首先执行$(..)中的内容,然后运行for循环。直到$(...)结束,它才能启动for
  • 它可能会溢出命令行缓冲区:shell执行$(..),然后将其放在命令行上。命令行缓冲区可能大约是32千字节,现在可能更多,但如果你有10000个文件,每个文件平均20个字符,你最终会得到超过200Kb的命令行缓冲
  • For循环在处理错误的文件名方面很糟糕:如果文件名中有空格,则每个单词都被视为一个文件

一个更好的构造是:

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

find也非常灵活。您可以将列表限制为仅文件、特定年龄范围内的文件等。最常见的需要复制for循环的文件有:

$ find . -depth 1

行为就像ls -a:

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

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

最新更新