Question

I've written a shell script that takes a directory as an arg and prints the file names and sizes, I wanted to find out how to add up the file sizes and store them so that I can print them after the loop. I've tried a few things but haven't gotten anywhere so far, any ideas?

#!/bin/bash

echo "Directory <$1> contains the following files:"
let "x=0"
TEMPFILE=./count.tmp
echo 0 > $TEMPFILE
ls $1 |
while read file
do
        if [ -f $1/$file ]
        then
                echo "file: [$file]"
        stat -c%s $file > $TEMPFILE
        fi
cat $TEMPFILE
done
echo "number of files:"
cat ./count.tmp

Help would be thoroughly appreciated.

Was it helpful?

Solution

A number of issues in your code:

Assuming you're just wanting to get practice at this and/or want to do something else other than what already does, you should change syntax to something like

#!/bin/bash

dir="$1"
[[ $dir == *'/' ]] || dir="$dir/"
if [[ -d $dir ]]; then
  echo "Directory <$1> contains the following files:"
else
  echo "<$1> is not a valid directory, exiting"
  exit 1
fi

shopt -s dotglob
for file in "$dir"*; do
  if [[ -f $file ]]; then
    echo "file: [$file]"
    ((size+=$(stat -c%s "$file")))
  fi
done

echo "$size"

Note:

  • You don't have to pre-allocate variables in bash, $size is assumed to be 0
  • You can use (()) for math that doesn't require decimal places.
  • You can use globs (*) to get all files (including dirs, symlinks, etc...) in a particular directory (and globstar ** for recursive)
  • shopt -s dotglob Is needed so it includes hidden .whatever files in glob matching.

OTHER TIPS

You can use ls -l to find size of files:

echo "Directory $1 contains the following:"
size=0
for f in "$1"/*; do
    if [[ ! -d $f ]]; then
        while read _ _ _ _ bytes _; do
            if [[ -n $bytes ]]; then
                ((size+=$bytes))
                echo -e "\tFile: ${f/$1\//} Size: $bytes bytes"
            fi
        done < <(ls -l "$f")
    fi
done
echo "$1 Files total size: $size bytes"

Parsing ls results for size is ok here as byte size will always be found in the 5th field.

If you know what the date stamp format for ls is on your system and portability isn't important, you can parse ls to reliably find both the size and file in a single while read loop.

echo "Directory $1 contains the following:"
size=0
while read _ _ _ _ bytes _ _ _ file; do
    if [[ -f $1/$file ]]; then
        ((size+=$bytes))
        echo -e "\tFile: $file Size: $bytes bytes"
    fi
done < <(ls -l "$1")
echo "$1 Files total size: $size bytes"

Note: These solutions would not include hidden files. Use ls -la for that.

Depending on the need or preference, ls can also print sizes in a number of different formats using options like -h or --block-size=SIZE.

#!/bin/bash

echo "Directory <$1> contains the following files:"

find ${1}/* -prune -type f -ls | \
    awk '{print; SIZE+=$7} END {print ""; print "total bytes: " SIZE}'

Use find with -prune (so it does not recurse into subdirectories) and -type f (so it will only list files and no symlinks or directories) and -ls (so it lists the files).

Pipe the output into awk and

for each line print the whole line (print; replace with print $NF to only print the last item of each line, which is the filename including the directory). Also add the value of the 7th field, which is the file size (in my version of find) to the variable SIZE.

After all lines have been processed (END) print the calculated total size.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top