Get metada of file with portable way

Published on: 2023-10-19

Try to use command stat with portable way

The command stat is not POSIX and if you'd like to use same syntax on UNIX machines as macOS, *BSD or Linux you have a problem. The interface is different for BSD derivates and Linux world. The reason is that BSD comes from AT&T code and Linux uses GNU coreutils which has some differences.

I met this problem when I wanted to get and check modification time of files. If you want to format output to show only modification time with stat comes from GNU/coreutils you'll write:


$ stat -c %Y file.txt
      

,but if you use stat from BSD, then:


$ stat -f %m file.txt
      

You can see that stat is different for both the option and formatting character.

Hack with awk or grep

Fortunately the BSD version of stat contains option -x, which caused the output will be same as in Linux. Thanks to that we can use awk or grep to get modification of timestamp.

grep


$ stat -x file.html | grep -i modify | cut -c 9-
      

awk


$ stat -x file.html | awk '/Modify:/ {printf $0}' | cut -c 9-
      

This approach has several drawbacks.

  1. you get modify time in format which is difficult to use for comparisons for example
  2. this output gives you only one type of time format and other format has to be get by other way
  3. you have to distinct if you use command stat on Linux or BSD derivate, because the option -x can be used only in BSD derivates

Summary

It is not recommended easy portable way how to get modification time.

Find out which type of stat is used

Other approach is to find out which version of stat is used and pass suitable option and formatting character to the command. See example below to get modification timestamp.


# GNU coreutils have option --version
if $(stat --version &>/dev/null)
then
  mod_time_fmt="-c %Y"
else
  mod_time_fmt="-f %m"
fi
      

By the code above we'll create portable version of stat. Except for example solaris which does not contain command stat at all.

Same approach can be used when we was hacking with awk or grep, but here we get timestamp in seconds,x which can be easily compared.

Same approach can be done with type of system, run uname to get information if platform is Linux, Darwin(macOS) or BSD.

Use language for scripting as Perl or Python

If you make something difficult or complicated you should leave scripting in shell and instead use language which was created for or language which is more suitable to achieve your goal. Perl and Python are scripting languages which are found on most systems (their scripts are very portable) and their syntax is much more friendly then shell scripting. They should contains any ordinary function which you need to get info about files.

If you still want to use only shell script you can use oneline call from shell script with scripting language. Run Perl or Python code in shell script can be done with run the certain interpreter with option -e resp. -c.


$ file=path/to/file.txt
$ perl -e "print((stat(\"$file\"))[9])"
$ python -c "import os; print(os.stat(\"$file\").st_mtime, end='')"
      

Check if one file is newer then other with POSIX

There is one POSIX solution to get information if FILE1 is newer than FILE2 (FILE1 -nt FILE2). The solution below is described in FreeBSD documentation.


$ test -n "$(find -L -- FILE1 -prune -newer FILE2 2>/dev/null)"
      

Get modification time with date command

Unfortunately it is not POSIX solution, but it can be used on Linux and macOS. Command date can show modification date with option -r. Other nice feature of date is formatting the output of time with leading plus character +. After plus char you specify wanted output format. See example below.


$ date -r file.txt +%Y-%m-%d
2023-10-20
$ date -r file.txt +%Y/%m
2023/10
      

Resources