根据文件名将文件排序到目录中

时间:2010-10-05 14:58:05

标签: linux bash

我有一些包含大量文件的目录。由于其中一些文件正在接近600k文件,因此它们已成为一个难以处理的主要问题。只列出文件正在慢慢成为处理它们的应用程序的主要瓶颈。

文件的名称如下: id_date1_date2.gz 我决定将文件拆分成几个较小的文件,具体取决于第一部分“id”。

由于相同的id可能会显示在大量文件中,并且相同的ID已经显示在几个目录中,我需要跟踪已复制的文件ID以及dirs。 否则我最终会复制疯狂的次数,或者从dir Y复制时丢失id X,如果已经从dir Z复制的话。

我写了一个脚本来完成这个。一些调试包括

#!/bin/bash  
find /marketdata -maxdepth 2 -type d | grep "[0-9]\.[0-9][0-9][0-9]$" | sort | #head -n2 | tail -n1 |
    while read baseDir; do

    cd $baseDir;
    echo $baseDir > tmpFile;
    find . -type f | grep -v "\.\/\." | #sort | head -n4 |
            while read file; do
            name=$(awk 'BEGIN {print substr("'"$file"'", 3,index("'"$file"'", "_")-3 )}');

            dirkey=${baseDir//[\/,.]/_}"_"$name;
            if [ "${copied[$dirkey]}" != "true" ]; then
                    echo "Copying $baseDir/$name with:";
                    echo mkdir -p $(sed 's/data/data4/' tmpFile)/$name;
                    #mkdir -p $(sed 's/data/data4/' tmpFile)/$name;
                    oldName=$baseDir/$name"_*";
                    echo cp $oldName "$(sed 's/data/data4/' tmpFile)/$name/";
                    #cp $oldName "$(sed 's/data/data4/' tmpFile)/$name/";
                    echo "Setting $dirkey to true";
                    copied[$dirkey]="true";
            else
                    echo "$dirkey: ${copied[$dirkey]}"
                    sleep 1
            fi
    done;

    rm tmpFile;
done

这里的问题是复制中的所有键的值似乎从第一次复制时变为真,因此我对bash数组的处理可能就是问题所在。

取得一些进展: 我尝试将每个密钥写入文件,并在每次迭代时,将该文件读入数组。这显然是非常丑陋,但看起来它实现了我的目标。可能因为我处理了几千个id而变得非常慢。稍后会更新。

对于将来可能会发现这一点的其他人,这是最终的脚本:

declare -A copied

find /marketdata -maxdepth 2 -type d -name "[0-9]\.[0-9][0-9][0-9]" | sort | #head -n3 | tail -n1 |
    while read baseDir; do

    cd $baseDir;
    find . -type f | grep -v "\.\/\." | sort | #head -n100 |
            while read file; do
            length=$(expr index "$file" "_");
            name=${file:2:$((length - 3))};

            dirkey=${baseDir//[\/,.]/_}"_"$name; 
            if [ "${copied[$dirkey]}" != "true" ]; then
                    echo "Copying ${baseDir}/${name} to ${baseDir//data/data4}/$name";
                    mkdir -p "${baseDir//data/data4}/$name";
                    oldName="${baseDir}/${name}_*";
                    cp -n $oldName "${baseDir//data/data4}/${name}/";
                    copied[$dirkey]="true";
            fi
    done;
done

没有awk,没有sed,更好的引用,没有写入临时文件到光盘,更少grep。 我不确定dirkey hack是否有必要,因为关联数组正常工作,我也不完全理解为什么我需要oldName var。

2 个答案:

答案 0 :(得分:1)

如果$dirkey中的值包含字母字符,则必须使用在Bash 4之前不可用的关联数组。如果您使用的是Bash 4且键是字母数字而不是简单的数字,在脚本顶部添加以下内容:

declare -A copied

补充意见:

您在某些地方使用参数展开,而在其他地方使用sed。您可以在(可能)所有情况下使用大括号扩展。

我建议不要像$var"literal"$var那样引用,而是像"${var}literal${var}"那样做,或者如果文字不会被模糊地解释为变量名称的一部分,你可以省略大括号:{{ 1}}。

使用"literal$var"代替复杂awk引用的变量:"'"

在循环中调用外部可执行文件会使事情变得非常缓慢,特别是如果它一次只处理一个值(或数据行)。 'sed awk -v awkvar=$shellvar '{print awkvar}' awk`命令可以转换为参数扩展形式。

GNU commands that I mentioned are examples of this. Also, your有一个正则表达式功能,您可以使用而不是find

应引用包含文件名的所有变量名称。

答案 1 :(得分:0)

cp的-n选项在这种情况下非常有用。如果文件已经在目的地,您可以放心。

-n, --no-clobber
   do not overwrite an existing file (overrides 
   a previous -i option)

这基本上就是说你在谈论你做同样的工作两次离开的情况。您可以将关注点拆分为移动所有文件,仅移动之前未移动过的文件。