1

私は問題があります。指定されたパスにあるすべてのファイルとディレクトリを検索し、結果に関する情報を表示する bash スクリプトを作成する必要があります。許容時間: 30 秒。

#!/bin/bash

DIRS=0
FILES=0
OLD_FILES=0
LARGE_FILES=0
TMP_FILES=0
EXE_FILES=0
IMG_FILES=0
SYM_LINKS=0
TOTAL_BYTES=0

#YEAR_AGO=$(date -d "now - 1 year" +%s)
#SECONDS_IN_YEAR=31536000

function check_dir {
    for entry in "$1"/*
    do
        if [ -d "$entry" ]; then
            ((DIRS+=1))
            check_dir "$entry"
        else if [ -f "$entry" ]; then
                ((FILES+=1))
                #SIZE=$(stat -c%s "$entry")
                #((TOTAL_BYTES+=SIZE))
                #CREATE_DATE=$(date -r "$entry" +%s)
                #CREATE_DATE=$(stat -c%W "$entry")
                #DIFF=$((CREATE_DATE-YEAR_AGO))
                #if [ $DIFF -ge $SECONDS_IN_YEAR ]; then
                #   ((OLD_FILES+=1))
                #fi
             fi

        fi
    done
}

if [ $# -ne 2 ]; then
    echo "Usage: ./srpt path emailaddress"
    exit 1
fi

if [ ! -d $1 ]; then
    echo "Provided path is invalid"
    exit 1
fi

check_dir $1

echo "Execution time $SECONDS"
echo "Dicrecoties $DIRS"
echo "Files $FILES"
echo "Sym links $SYM_LINKS"
echo "Old files $OLD_FILES"
echo "Large files $LARGE_FILES"
echo "Graphics files $IMG_FILES"
echo "Temporary files $TMP_FILES"
echo "Executable files $EXE_FILES"
echo "Total file size $TOTAL_BYTES"

上記のコメント行で実行した結果は次のとおりです。

Execution time 1
Dicrecoties 931
Files 14515
Sym links 0
Old files 0
Large files 0
Graphics files 0
Temporary files 0
Executable files 0
Total file size 0

コメントを削除する場合

SIZE=$(stat -c%s "$entry")
((TOTAL_BYTES+=SIZE))

私が得た:

Execution time 31
Dicrecoties 931
Files 14515
Sym links 0
Old files 0
Large files 0
Graphics files 0
Temporary files 0
Executable files 0
Total file size 447297022

31秒。スクリプトを高速化するにはどうすればよいですか? さらに+30秒で、1年以上作成された日付のファイルを見つけることができます

4

3 に答える 3

5

多くの場合、シェルでループを使用することは、間違ったアプローチをとっていることを示しています。

シェルは、他のツールを実行するためのすべてのツールの前にあります。

数えることはできますが、それを行うawkためのより優れたツールです。

ファイルを一覧表示して検索することはできますが、そのfind方が優れています。

最高のシェル スクリプトとは、何百万ものツールを順番に起動し、すべての作業をシェルが行うスクリプトではなく、いくつかのツールがタスクに貢献するスクリプトです。

ここでは、通常、findファイルを見つけて必要なすべてのデータを収集し、それをawkむしゃむしゃ食べて統計を返す方法がより適切です。ここでは、GNUfindと GNU awk( for RS='\0') および GNU date( for ) を使用しています-d

find . -printf '%y.%s.%Ts%p\0' |
  awk -v RS='\0' -F'[.]' -v yearago="$(date -d '1 year ago' +%s)" '
    {
      type[$1]++; 
      if ($1 == "f") {
        total_size+=$2
        if ($3 < yearago) old++
        if (!index($NF, "/")) ext[tolower($NF)]++
      }
    }
    END {
      printf("%20s: %d\n", "Directories", type["d"])
      printf("%20s: %d\n", "Total size", total_size)
      printf("%20s: %d\n", "old", old)
      printf("%20s: %d\n", "jpeg", ext["jpg"]+ext["jpeg"])
      printf("%20s: %d\n", "and so on...", 0)
    }'
于 2013-06-23T21:41:20.447 に答える
2

あなたがしたように出力を解析することは避けたいと思うでしょうfind(私のコメントを見てください):ファイル名にスペースがあると壊れます。

$(stat ...)or$(date ...)ステートメントのような外部プロセスへの fork は絶対に避けたいでしょう。各 fork には多くのコストがかかります!

それfind自体でかなり多くのことができることがわかりました。たとえば、ファイル、ディレクトリ、およびリンクの数を数えたい場合。

私たちは皆、での単純な方法を知っています (ほとんどの場合、あなたが行ったことです):

#!/bin/bash

shopt -s globstar
shopt -s nullglob
shopt -s dotglob
nbfiles=0
nbdirs=0
for f in ./**; do
    [[ -f $f ]] && ((++nbfiles))
    [[ -d $f ]] && ((++nbdirs))
done
echo "There are $nbdirs directories and $nbfiles files, and we're very happy."

警告。この方法では、リンク先に従ってリンクをカウントします。ファイルへのリンクはファイルとしてカウントされます。

方法はどうfindですか?ファイル、ディレクトリ、および (シンボリック) リンクの数をカウントします。

#!/bin/bash

nbfiles=0
nbdirs=0
nblinks=0
while read t n; do
    case $t in
    dirs) ((nbdirs+=n+1)) ;;
    files) ((nbfiles+=n+1)) ;;
    links) ((nblinks+=n+1)) ;;
    esac
done < <(
    find . -type d -exec bash -c 'echo "dirs $#"' {} + \
         -or -type f -exec bash -c 'echo "files $#"' {} + \
         -or -type l -exec bash -c 'echo "links $#"' {} + 2> /dev/null
)
echo "There are $nbfiles files, $nbdirs dirs and $nblinks links. You're happy to know aren't you?"

同じ原則で、連想配列、より多くのフィールド、より複雑findなロジックを使用します。

#!/bin/bash

declare -A fields

while read f n; do
    ((fields[$f]+=n))
done < <(
    find . -type d -exec bash -c 'echo "dirs $(($#+1))"' {} + \
        -or -type f -exec bash -c 'echo "files $(($#+1))"' {} + -printf 'size %s\n' \
            \( \
                \( -iname '*.jpg' -printf 'jpg 1\n' -printf 'jpg_size %s\n' \) \
                -or -size +100M -printf 'large 1\n' \
            \) \
        -or -type l -exec bash -c 'echo "links $(($#+1))"' {} + 2> /dev/null
)

for f in "${!fields[@]}"; do
    printf "%s: %s\n" "$f" "${fields[$f]}"
done

これがあなたにいくつかのアイデアを与えることを願っています!幸運を!

于 2013-06-23T21:28:58.187 に答える