How to check file metadata using shell script

It is well described how to verify file existence or type, but there is much more information that can be easily verified inside shell script using nothing more then simple stat command.

Table of contents

Size

I will start with the simplest thing to check, which is the file size.

#!/bin/sh
# print file size

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -f "$file" ]; then
  size=$($stat_cmd --format="%s" $file)  # bytes
  units="B"

  if [ "$size" -gt "1024" ]; then
    size=$(expr $size / 1024)            # kilobytes
    units="KB"
  fi

  if [ "$size" -gt "1024" ]; then
    size=$(expr $size / 1024)            # megabytes
    units="MB"
  fi

  if [ "$size" -gt "1024" ]; then
    size=$(expr $size / 1024)            # gigabytes
    units="GB"
  fi

  if [ "$size" -gt "1024" ]; then
    size=$(expr $size / 1024)            # terabytes
    units="TB"
  fi

  echo "$file size is $size $units"
else
  echo "File does not exist"
fi

Type

The next thing that can be easily verified is file type.

#!/bin/sh
# print file type

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -e "$file" ]; then
  type=$(LC_ALL=C $stat_cmd --format="%F" $file)

  echo -n "$file type is "
  case $type in
    "regular file"|"directory"|"symbolic link"|"fifo"|"character special file"|"block special file"|"socket")
      echo $type
      ;;
    *)
      echo "unknown"
  esac
else
  echo "File does not exist"
fi

The above shell script is very descriptive, the equivalent solution would be to use human readable form of permissions.

#!/bin/sh
# alternative way to print file type

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -e "$file" ]; then
  type=$($stat_cmd --format="%A" $file | cut -c 1)

  echo -n "$file type is "
  case $type in
    "-") echo "regular file";;
    "d") echo "directory";;
    "l") echo "symbolic link";;
    "p") echo "fifo";;
    "c") echo "character special file";;
    "b") echo "block special file";;
    "s") echo "socket";;
    *)   echo "unknown"
  esac
else
  echo "File does not exist"
fi

Ownership

Owner

You can quickly verify file owner using the following constructs.

#!/bin/sh
# print and verify file owner

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -e "$file" ]; then
  user_id="$($stat_cmd --format="%u" $file)"
  user_name="$($stat_cmd --format="%U" $file)"

  echo "$file owner is $user_name ($user_id)"

  # verify UID
  if [ "$user_id" -ge "1000" ]; then
    echo "This file belongs to regular user"
  else
    echo "This file belongs to system user"
  fi

  # verify user name
  if [ "$user_name" = "milosz" ]; then
    echo "This file belongs to milosz user"
  else
    echo "This file does not belong to milosz user"
  fi
else
  echo "File does not exist"
fi

Group

File group can be verified in the same way as the above one.

#!/bin/sh
# print and verify file group

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -e "$file" ]; then
  group_id="$($stat_cmd --format="%g" $file)"
  group_name="$($stat_cmd --format="%G" $file)"

  echo "$file group is $group_name ($group_id)"

  # verify GID
  if [ "$group_id" -ge "1000" ]; then
    echo "This file belongs to regular group"
  else
    echo "This file belongs to system group"
  fi

  # verify group name
  if [ "$group_name" = "root" ]; then
    echo "This file belongs to root group"
  else
    echo "This file does not belong to root group"
  fi
else
  echo "File does not exist"
fi

Permissions

This one is more interesting as you can use octal or human readable representation.

Verify permissions

The simplest way to verify permissions is to use octal representation.

#!/bin/sh
# check file permissions

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -e "$file" ]; then
  if [ "$($stat_cmd --format="%a" $file | xargs printf "%04d")" = "0755" ]; then
   echo "$file 0755 proper permission confirmed"
  fi
else
  echo "File does not exist"
fi

Analyse permissions using human readable representation

You can simply translate human readable representation [-rwxr-xr-x].

#!/bin/sh
# analyse file permissions

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -e "$file" ]; then
  # parse "-rwxr-xr-x" format
  # skip first character (file type)
  rights=$($stat_cmd --format="%A" $file | cut -c 2-)     #
  for class in $(seq 1 3); do                             # rwx r-x r-x
    case $class in                                        # o   g   o
      1)                                                  # w   r   t
       echo " * owner"                                    # n   o   h
       ;;                                                 # e   u   e
      2)                                                  # r   p   r
       echo " * group"                                    #
       ;;
      3)
       echo " * other"
       ;;
    esac

    # range to cut for each class
    from=$(expr 3 \* $class - 2)    #   (1) 1   (2) 4   (3) 7
    to=$(expr 3 \* $class)          #       3       6       9

    # 1st step rwx, 2nd step r-x, 3rd step r-x
    class_perm=$(echo $rights | cut -c ${from}-${to})
    for pos in $(seq 1 3); do
      mode=$(echo $class_perm | cut -c $pos)
      case $mode in
        "r")
           echo "   read"
           ;;
        "w")
           echo "   write"
           ;;
        "x")
           echo "   execute"
           ;;
        "s")
           if [ "$class" = 1 ]; then
             echo "   execute and SUID"
           else
             echo "   execute and SGID"
           fi
           ;;
        "S")
           if [ "$class" = 1 ]; then
             echo "   SUID"
           else
             echo "   SGID"
           fi
           ;;
        "t")
           echo "   execute and sticky bit"
           ;;
        "T")
           echo "   sticky bit"
           ;;
      esac
    done
  done
else
  echo "File does not exist"
fi

Analyse permissions using octal representation

I prefer this way as this is a quite interesting solution - read octal representation, convert to binary, then translate.

#!/bin/sh
# check file permissions

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -e "$file" ]; then
  # parse "0755" format, convert "755" to "0755"
  rights=$($stat_cmd --format="%a" $file | xargs printf "%04d")
  for class in $(seq 0 3); do                                 #
    case $class in                                            # 0 7 5 5
      0)                                                      # s o g o
       echo " * special"                                      # p w r t
       ;;                                                     # e n o h
      1)                                                      # c e u e
       echo " * owner"                                        # i r p r
       ;;                                                     # a
      2)                                                      # l
       echo " * group"                                        #
       ;;
      3)
       echo " * other"
       ;;
    esac

    class_pos=$(expr $class + 1)                   # class position 1..4
    class_perm=$(echo $rights | cut -c $class_pos) # octal class rep

    perm_bin=$(echo "obase=2; $class_perm" | bc | xargs printf "%03d")
    # convert it to binary
    # 1st step 111 (7), 2nd step 101 (5), 3rd step 101 (5)

    # parse binary representation
    for pos in $(seq 1 3); do
      mode=$(echo $perm_bin | cut -c $pos)
      if [ "$class" = "0" ]; then                      # special
        if   [ "$mode" = 1 -a "$pos" = "1" ]; then     # [1]XX
          echo "   SUID";
        elif [ "$mode" = 1 -a "$pos" = "2" ]; then     # X[1]X
          echo "   SGID";
        elif [ "$mode" = 1 -a "$pos" = "3" ]; then     # XX[1]
          echo "   sticky bit";
        fi
      else                                             # regular
        if   [ "$mode" = 1 -a "$pos" = "1" ]; then     # [1]XX
          echo "   read";
        elif [ "$mode" = 1 -a "$pos" = "2" ]; then     # X[1]X
          echo "   write";
        elif [ "$mode" = 1 -a "$pos" = "3" ]; then     # XX[1]
          echo "   execute";
        fi
      fi
    done
  done
else
  echo "File does not exist"
fi

Time of last access and modification

Print times

You can simply display times using following code.

#!/bin/sh
# print file last access and modification date

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -f "$file" ]; then
  mod_time=$($stat_cmd --format="%y" $file)
  acc_time=$($stat_cmd --format="%x" $file)

  echo "$file last access is $acc_time, modification time is $mod_time"
else
  echo "File does not exist"
fi

Compare times

Again, the most interesting part is date comparison using seconds since Epoch.

#!/bin/sh
# compare file last access and modification date with contrived date values

# stat command
stat_cmd=$(which stat)

# file to be verified
file=$1

if [ -e "$file" ]; then
  mod_time=$($stat_cmd --format="%Y" $file)
  acc_time=$($stat_cmd --format="%X" $file)

  if [ "$mod_time" -ge "$(date -d "00:00" +"%s")" ]; then
    echo "$file was modified today"
  fi

  if [ "$acc_time" -ge "$(date -d "last day" +"%s")" ]; then
    echo "$file was accessed at least yesterday"
  fi

  if [ "$mod_time" -ge "$(date -d "2014-11-09 19:00" +"%s")" -a  "$mod_time" -le "$(date -d "2014-11-09 20:00" +"%s")" ]; then
    echo "$file was modified 2014-11-09 between 19:00 and 20:00"
  fi

  if [ "$acc_time" -lt "$(date -d "-3 days" +"%s")" ]; then
    echo "$file was accessed more then three days ago"
  fi
else
  echo "File does not exist"
fi

Ending notes

You can perform more interesting things using techniques shown here, all I can do now is to suggest to check out stat manual page and GNU coreutils manual.

Milosz Galazka's Picture

About Milosz Galazka

Milosz is a system administrator working for a successful Polish company and a long time supporter of Free Software Foundation and Debian operating system.

Gdansk, Poland https://sleeplessbeastie.eu