12

I'm using bash to build a script where I will get a filename in a variable an then with this variable get the file unix last modification date.

I need to get this modification date value and I can't use stat command.

Do you know any way to get it with the common available *nix commands?

j0k
  • 22,600
  • 28
  • 79
  • 90
odew
  • 581
  • 2
  • 8
  • 18
  • If possible with a configurable date format. – odew Jun 26 '12 at 17:20
  • 1
    And why can't you use `stat`? – Sean Bright Jun 26 '12 at 17:22
  • stat is not available in the environment I'm currently working. – odew Jun 26 '12 at 17:25
  • If my answer doesn't work (`date` doesn't support the `-r` flag), could you provide more specific platform information - you've tagged with `Linux` and `Unix`; `stat` and `date` are both (as far as I know) pretty widespread on the various Linux distributions I've come across. – simont Jun 26 '12 at 21:50
  • I need a portable or robust solution, I'm in a HP-UX system and this script should also run in Linux. No perl also. – odew Jun 27 '12 at 08:52

5 Answers5

18

Why you shouldn't use ls:

Parsing ls is a bad idea. Not only is the behaviour of certain characters in filenames undefined and platform dependant, for your purposes, it'll mess with dates when they're six months in the past. In short, yes, it'll probably work for you in your limited testing. It will not be platform-independent (so no portability) and the behaviour of your parsing is not guaranteed given the range of 'legal' filenames on various systems. (Ext4, for example, allows spaces and newlines in filenames).

Having said all that, personally, I'd use ls because it's fast and easy ;)

Edit

As pointed out by Hugo in the comments, the OP doesn't want to use stat. In addition, I should point out that the below section is BSD-stat specific (the %Sm flag doesn't work when I test on Ubuntu; Linux has a stat command, if you're interested in it read the man page).

So, a non-stat solution: use date

date, at least on Linux, has a flag: -r, which according to the man page:

display the last modification time of FILE

So, the scripted solution would be similar to this:

date -r ${MY_FILE_VARIABLE}

which would return you something similar to this:

zsh% date -r MyFile.foo
Thu Feb 23 07:41:27 CST 2012

To address the OP's comment:

If possible with a configurable date format

date has a rather extensive set of time-format variables; read the man page for more information.

I'm not 100% sure how portable date is across all 'UNIX-like systems'. For BSD-based (such as OS X), this will not work; the -r flag for the BSD-date does something completely different. The question doesn't' specify exactly how portable a solution is required to be. For a BSD-based solution, see the below section ;)

A better solution, BSD systems (tested on OS X, using BSD-stat; GNU stat is slightly different but could be made to work in the same way).

Use stat. You can format the output of stat with the -f flag, and you can select to display only the file modification data (which, for this question, is nice).

For example, stat -f "%m%t%Sm %N" ./*:


1340738054  Jun 26 21:14:14 2012 ./build
1340738921  Jun 26 21:28:41 2012 ./build.xml
1340738140  Jun 26 21:15:40 2012 ./lib
1340657124  Jun 25 22:45:24 2012 ./tests

Where the first bit is the UNIX epoch time, the date is the file modification time, and the rest is the filename.

Breakdown of the example command

stat -f "%m%t%Sm %N" ./*

  1. stat -f: call stat, and specify the format (-f).
  2. %m: The UNIX epoch time.
  3. %t: A tab seperator in the output.
  4. %Sm: S says to display the output as a string, m says to use the file modification data.
  5. %N: Display the name of the file in question.

A command in your script along the lines of the following:

stat -f "%Sm" ${FILE_VARIABLE}

will give you output such as:

Jun 26 21:28:41 2012

Read the man page for stat for further information; timestamp formatting is done by strftime.

simont
  • 68,704
  • 18
  • 117
  • 136
  • 1
    I'd upvote you for the "don't use ls" part, but the op **did** say he couldn't use `stat` since he needs this to be portable to any unix-like OS. – WhyNotHugo Jun 26 '12 at 21:29
  • `date` does not work on BSD (tested on OpenBSD). `stat` does, but doesn't work on linux. – WhyNotHugo Jun 26 '12 at 21:53
  • @Hugo Yep. As noted in the answer; `date` isn't portable to BSD systems, which is why I've left the `stat` section in the answer. I'm not certain there's a portable solution; answer containing `find` isn't portable to my BSD systems, either. – simont Jun 26 '12 at 21:54
  • 2
    You complain that `ls -l` isn't portable, but `date` is even less portable. The problem is that BSD systems also use the `-r` parameter, but for different purposes. In other words, using the `-r` may end up being a valid command (exit status = 0), but not get the results you want. – David W. Jun 27 '12 at 03:32
  • @DavidW. The answer mentions that `date` isn't portable to BSD-based systems. Using `ls` isn't portable **because it's not guaranteed to work** on *any* platform. This contrasts to both `date` and `stat` which aren't portable because the *documented* implementation of the tools differs from platform to platform - they're different tools. `ls` is the *same* tool with undefined behaviour for *legal inputs*. (I did say personally I'd use `ls`, though...) I'm not convinced there's a good, cross-platform solution to this type of problem. – simont Jun 27 '12 at 21:43
  • Is "GNU stat is slightly different but could be made to work in the same way" correct? GNU stat uses `%Y` for BSD stat's `%m`, so I don't see how you can get the modified time with one command that works on BSD or GNU systems. – sargas Aug 26 '15 at 20:09
  • If `current` is a symlink to a directory, `stat current` will give info on the link itself, `stat current/` will give info on the linked directory, however both `date -f current` and `date -f current/` will give info on the symlinked directory, so you cannot get time of the symlink itself by using `date`. – Jānis Elmeris Nov 24 '15 at 13:23
4

have perl?

perl -MFile::stat -e "print scalar localtime stat('FileName.txt')->mtime"
clt60
  • 62,119
  • 17
  • 107
  • 194
3

How about:

find $PATH -maxdepth 1 -name $FILE -printf %Tc

See the find manpage for other values you can use with %T.

Alex Howansky
  • 50,515
  • 8
  • 78
  • 98
1

You can use the "date" command adding the desired format option the format:

 date +%Y-%m-%d -r /root/foo.txt

2013-05-27

 date +%H:%M -r /root/foo.txt

23:02

RichTea
  • 1,462
  • 10
  • 16
  • 24
-2

You can use ls -l which lists the last modification time, and then use cut to cut out the modification date:

mod_date=$(ls -l $file_name | cut -c35-46)

This works on my system because the date appears between columns 35 to 46. You might have to play with it on your system.

The date is in two different formats:

  • Mmm dd hh:mm
  • Mmm dd yyyy

Files modified more than a year ago will have the later format. Files modified less than a year ago will have to first format. You could search for a ":" and know which format the file is in:

if echo "$mod_date" | grep -q ":"
then
    echo "File was modified within the year"
else
    echo "File was modified more than a year ago"
fi
David W.
  • 105,218
  • 39
  • 216
  • 337
  • Parsing `ls` is [generally considered bad practise](http://mywiki.wooledge.org/ParsingLs). While it (probably) works fine, it's not guaranteed, and can't be considered portable or robust. – simont Jun 26 '12 at 20:49
  • @simont And the solution? You are suppose to use general shell script and more or less standard tools which I take it to mean you can't use a scripting language like Perl or Python either. You also can't use stat. With those restrictions, there's not much left except for parsing `ls`. As I stated in my post, it might not work exactly the same on the OP's system. – David W. Jun 27 '12 at 03:26