4

how could I find all the files and directories with size larger than or equal to the average file size of the current directory?

  • 1
    Do you want to include only the files in the top level directory or also any files from subdirectories? – terdon Jan 26 '16 at 11:50
  • @terdon only top level – radha Jan 26 '16 at 11:54
  • what do you mean by "...directories with size ...", when you also say "only the top level directory" ? Is the scope just files in the current directory? – Jeff Schaller Jan 26 '16 at 13:31
  • What average, mean ? median ? – 123 Jan 26 '16 at 16:01
  • If any of the existing answers solves your problem, please consider accepting it via the checkmark. Thank you! – Jeff Schaller Apr 25 '17 at 10:37
3
avg_size=$(find . -maxdepth 1 -type f -printf %s\\n | 
  { sum=0; files=0; while read size; do sum=$((sum+size)); ((files++)); done; echo "$((sum/files))"; })
echo "average file size: ${avg_size}"
find . -maxdepth 1 -type f -size +"$avg_size"c
2

If you have GNU tools:

find -maxdepth 1 -type f -printf '%s %p\0' | 
  awk -v RS='\0' '{a[$0]=$1;s+=$1;}
                  END{m=s/NR; for(i in a){if(a[i]>=m){print i}}}' 

And if you don't:

perl -le 'opendir(D,"."); @F=readdir(D); @files=grep{-f $_}@F; 
    for (@files){$s= -s $_; $t+=$s; $f{$_}=$s} 
    print join "\n",grep{$f{$_}>=$t/scalar(@files)}@files' 

Which can be expanded to:

#!/usr/bin/perl 

## Open the current directory as D
opendir(D,".");
## Read the contents of D into @F
@F=readdir(D);
## Collect only the files, no dirs etc. 
@files=grep{-f $_}@F;
## For each file, as $_
for (@files){
    ## Get the size
    $s= -s $_;
    ## Add the size to the total
    $t+=$s;
    ## Save the size in the hash %f whose keys are the file names
    $f{$_}=$s
}
## Get all files whose size is greater than the average
my @wanted=grep{$f{$_}>=$t/scalar(@files)}@files;
## Print the elements of the array @wanted with a newline after each of them
print join "\n", @wanted ;
print "\n";
  • if I'm reading your bash script correctly, you're indexing the files array with the sizes from the stat command, which could easily collide (touch a b). Also, I think your awk END{} has the comparison reversed -- OP wants the files /larger/ than the mean – Jeff Schaller Jan 26 '16 at 13:52
  • @JeffSchaller very good points, thanks. I fixed the awk and replaced the bash with a Perl version. – terdon Jan 26 '16 at 14:22
  • @StéphaneChazelas fair enough, thanks. The original version was wrong anyway, I had a stupid bug. – terdon Jan 27 '16 at 10:43
1

Here's a bash-specific solution; it loops over the files, gathering their sizes and names into an indexed bash array, then computes the average and loops back over the array, printing only those filenames that are strictly greater than the (integer) average size.

#!/bin/bash

declare -i sum=0
declare -i nfiles=0
declare -a filenames
declare -a filesizes

for file in *
do
  [ -f "$file" ] || continue
  size=$(stat -c %s -- "$file")
  filenames[$nfiles]="$file"
  filesizes[$nfiles]=$size
  sum+=$size
  nfiles+=1
done

[ $nfiles -eq 0 ] && exit

avg=$(( sum / nfiles ))

for((index=0; index < ${#filenames[*]}; ++index))
do
  [ ${filesizes[$index]} -gt $avg ] && printf "%s\n" "${filenames[$index]}"
done
0
#!/bin/bash
no=$(ls -l | wc -l)
find_files(){
    if [ $(ls -l | awk '{print $5}' | awk 'FNR=='$1'') -ge $2 ]
    then
        echo $(ls -l | awk '{print $9}' | awk 'FNR=='$1'')
    fi
}

average(){
    local   count=2
    sum=0
    while [ $count -le $no  ]
    do
        let sum=sum+$(ls -l | awk '{print $5}' | awk 'FNR=='$count'')

        ((count++))
    done
    let num=$no-1
    let avg=$sum/$num
    echo $avg
}
main(){
    a=$(average)
    i=2
    while [ $i -le $no ]
    do
        find_files $i $a
        ((i++))
    d

}

main

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.