File modification time (seconds) on Unix
On Unix, is there a command to display a file’s modification time, precise to the second? On Linux this is easily done with a «stat -c %y», which returns something like 2009-11-27 11:36:06.000000000 +0100 . I found no equivalent on Unix.
Ok, so if I write a program which uses an API call with parameters it’s ok to ask here. But if I write a program which calls another program with parameters it’s not? Is that what you’re saying?
12 Answers 12
Which exports something like this:
root:~# ls --time-style='+%d-%m-%Y %H:%M:%S' -l total 0 -rw-r--r-- 1 root root 0 16-04-2015 23:14:02 other-file.txt -rw-r--r-- 1 root root 0 16-04-2015 23:13:58 test.txt
Worked fine on SmartOS, which is shipped with a GNU ls. So if you need it on Solaris, you should be able to get the same result by using the GNU version of ls.
Prints — — ls: illegal option — — ls: illegal option — y ls: illegal option — = ls: illegal option — + ls: illegal option — % ls: illegal option — — ls: illegal option — % ls: illegal option — — ls: illegal option — % ls: illegal option — Y ls: illegal option — ls: illegal option — % ls: illegal option — : ls: illegal option — % ls: illegal option — M ls: illegal option — : ls: illegal option — %
According to the man page on my Mac (which has the BSD standard version of stat) you can get the epoch time version of the modification in seconds with:
Or if you want to print that out in hours:mins:secs you can do this:
perl -e "print scalar(localtime(`stat -f %m /etc/passwd`))"
Most Unix flavors have BSD style commands (of which stat is one) Linux has GNU style commands (which has a stat with different switches). If your Unix flavor doesn’t have stat at all you’d better tell us which Unix you’re using.
perl has a lstat() function, no need to call the stat utility from there. (lstat(«file»))[9] for the mtime. To format the date with BSD stat : stat -t ‘%F %T %z’ -f %Sm file
The following gives you last modified time in seconds since Epoch:
That’s specific to the GNU implementation of stat . Other stat implementations include (in chronological order): IRIX stat, zsh stat builtin, BSD stat.
The find command is a good source for all kinds of file information, including modification time to the second:
find /etc/passwd -maxdepth 0 -printf "%TY/%Tm/%Td %TH:%TM:%.2TS\n" 2011/11/21 13:41:36
The first argument can be a file. The maxdepth prevents searching if a directory name is given. The %T instructs it to print last modification time.
Some systems interpret %TS as a floating point seconds (e.g. 36.8342610). If you want fractional seconds use «%TS» instead of «%.2TS», but you may not see fractional seconds on every system.
One up-vote for a good idea. One down-vote for not considering the question was about a command in Unix. The printf option isn’t available in find on Solaris or MacOS because as @StephaneChazelas says it’s GNU-specific.
For anyone facing the same issue, I found no solution (on HP-UX 11i anyway). Ended up coding a personalized «ls -lh» for my needs. It’s not that hard.. Prints something like :
- 664 rw-/rw-/r-- 1L expertNoob adm 8.37 kB 2010.08.24 12:11:15 findf1.c d 775 rwx/rwx/r-x 2L expertNoob adm 96 B 2010.08.24 15:17:37 tmp/ - 775 rwx/rwx/r-x 1L expertNoob adm 16 kB 2010.08.24 12:35:30 findf1 - 775 rwx/rwx/r-x 1L expertNoob adm 24 kB 2010.09.14 19:45:20 dir_info - 444 r--/r--/r-- 1L expertNoob adm 9.01 kB 2010.09.01 11:23:41 getopt.c - 664 rw-/rw-/r-- 1L expertNoob adm 6.86 kB 2010.09.01 11:24:47 getopt.o - 664 rw-/rw-/r-- 1L expertNoob adm 6.93 kB 2010.09.14 19:37:44 findf1.o l 775 rwx/rwx/r-x 1L expertNoob adm 6 B 2010.10.06 17:09:01 test1 -> test.c - 664 rw-/rw-/r-- 1L expertNoob adm 534 B 2009.03.26 15:34:23 > test.c d 755 rwx/r-x/r-x 25L expertNoob adm 8 kB 2009.05.20 15:36:23 zip30/
#include #include #include #include #include #include #include #include #include #include //#include #include // PATH_MAX #include #include "getopt.h" static short START_VSNBUFF=16; // This is bformat from Better String library (bstrlib), customized int strformat (char ** str, const char * fmt, . ) < va_list arglist; char * buff; int n, r; /* Since the length is not determinable beforehand, a search is performed using the truncating "vsnprintf" call (to avoid buffer overflows) on increasing potential sizes for the output result. */ if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF; if ( NULL == ( buff = (char *) malloc((n + 2)*sizeof(char)) ) ) < n = 1; if ( NULL == ( buff = (char *) malloc((n + 2)*sizeof(char)) ) ) < fprintf( stderr, "strformat: not enough memory to format string\n" ); return -1; >> for (;;) < va_start (arglist, fmt); r = vsnprintf (buff, n + 1, fmt, arglist); // n+1 chars: buff[0]..buff[n], n chars from arglist: buff[n]='\0' va_end (arglist); buff[n] = (unsigned char) '\0'; // doesn't hurt, especially strlen! if ( strlen(buff) < n ) break; if (r >n) n = r; else n += n; if ( NULL == ( buff = (char *) realloc( buff, (n + 2)*sizeof(char) ) ) ) < free(buff); fprintf( stderr, "strformat: not enough memory to format string\n" ); return -1; >> if( NULL != *str ) free(*str); *str = buff; return 0; > int printFSObjectInfo( const char * path, const char * name ) < struct stat statbuf; struct passwd *pwd; struct group *grp; struct tm *tm; char datestring[256]; char *type = "? "; char *fbuf = NULL; double size = 0; const char *units[] = ; int i = 0; char owner[] = "---", group[] = "---", others[] = "---"; /* Get entry's information. */ if ( -1 == lstat( path, &statbuf ) ) < fprintf( stderr, "printFSObjectInfo: error: can't stat %s\n", path ); if( 0 == strformat( &fbuf, "lstat() said: %s", path ) ) < perror(fbuf); return -1; >> // File type if( S_ISREG(statbuf.st_mode) ) type = "-"; // regular file if( S_ISDIR(statbuf.st_mode) ) < // directory type="d"; if( S_ISCDF(statbuf.st_mode) ) type = "hd"; // hidden dir >if( S_ISBLK(statbuf.st_mode) ) type = "b"; // block special if( S_ISCHR(statbuf.st_mode) ) type = "c"; // character special if( S_ISFIFO(statbuf.st_mode) ) type = "f"; // pipe or FIFO if( S_ISLNK(statbuf.st_mode) ) type = "l"; // symbolic link if( S_ISSOCK(statbuf.st_mode) ) type = "s"; // socket if( S_ISNWK(statbuf.st_mode) ) type = "n"; // network special printf( "%2s ", type ); /* Print out type, permissions, and number of links. */ //printf("%10.10s", sperm (statbuf.st_mode)); if( S_IRUSR & statbuf.st_mode ) owner[0] = 'r'; if( S_IWUSR & statbuf.st_mode ) owner[1] = 'w'; if( S_IXUSR & statbuf.st_mode ) owner[2] = 'x'; if( S_IRGRP & statbuf.st_mode ) group[0] = 'r'; if( S_IWGRP & statbuf.st_mode ) group[1] = 'w'; if( S_IXGRP & statbuf.st_mode ) group[2] = 'x'; if( S_IROTH & statbuf.st_mode ) others[0] = 'r'; if( S_IWOTH & statbuf.st_mode ) others[1] = 'w'; if( S_IXOTH & statbuf.st_mode ) others[2] = 'x'; //printf( "\n%o\n", statbuf.st_mode ); printf( "%3o %s/%s/%s ", 0777 & statbuf.st_mode, owner, group, others ); printf("%4dL", statbuf.st_nlink); /* Print out owner's name if it is found using getpwuid(). */ if ((pwd = getpwuid(statbuf.st_uid)) != NULL) printf(" %-8.8s", pwd->pw_name); else printf(" %-8d", statbuf.st_uid); /* Print out group name if it is found using getgrgid(). */ if ((grp = getgrgid(statbuf.st_gid)) != NULL) printf(" %-8.8s", grp->gr_name); else printf(" %-8d", statbuf.st_gid); /* Print size of file. */ //printf(" %9d", (int)statbuf.st_size); i = 0; size = (double) statbuf.st_size; while (size >= 1024) < size /= 1024; i++; >if( 0 == (double)(size - (long) size) ) printf( "%7d %-2s", (long)size, units[i] ); else printf( "%7.2f %-2s", size, units[i] ); tm = localtime(&statbuf.st_mtime); /* Get localized date string. */ strftime(datestring, sizeof(datestring), "%Y.%m.%d %T", tm); // nl_langinfo(D_T_FMT) if ( 0 == strcmp(name, "\n") ) printf(" %s > %s", datestring, path); else < if( 0 == strcmp(type, "d") ) printf(" %s %s/", datestring, name); else printf(" %s %s", datestring, name); >if( 0 == strcmp(type, "l") ) < char buf[1+PATH_MAX]; if( -1 == readlink( path, buf, (1+PATH_MAX) ) ) < fprintf( stderr, "printFSObjectInfo: error: can't read symbolic link %s\n", path); if( 0 == strformat( &fbuf, "readlink() said: %s:", path ) ) < perror(fbuf); return -2; >> else < lstat( buf, &statbuf ); // want errno, a symlink may point to non-existing object if(errno == ENOENT) printf(" ->%s [!no such file!]\n", buf ); else < printf(" ->%s\n", buf ); if ( 0 != strcmp(name, "\n") ) printFSObjectInfo( buf, "\n" ); > > > else printf("\n"); return 0; > int main(int argc, char **argv) < struct dirent *dp; struct stat statbuf; char *path = NULL; //[1+PATH_MAX]; char *fbuf = NULL; char *pathArg = NULL; if( argc == 1 || 0 == strlen(argv[1]) ) pathArg = "."; else pathArg = argv[1]; if ( lstat( pathArg, &statbuf ) == -1 ) < printf("%s: error: can't stat %s\n", argv[0], pathArg); if( 0 == strformat( &fbuf, "stat() said: %s", pathArg ) ) perror(fbuf); exit(2); >if( S_ISDIR(statbuf.st_mode) ) < DIR *dir = opendir( pathArg ); if( NULL == dir ) < fprintf( stderr, "%s: error: can't open %s\n", argv[0], pathArg ); if( 0 != strformat( &fbuf, "opendir() said: %s", pathArg ) ) exit(5); perror(fbuf); exit(4); >/* Loop through directory entries. */ while ( (dp = readdir(dir)) != NULL ) < if( 0!= strformat( &path, "%s/%s", pathArg, dp->d_name ) ) continue; printFSObjectInfo( path, dp->d_name ); > closedir(dir); > else printFSObjectInfo( pathArg, pathArg ); return 0; >
In printFSObjectInfo() you have full functionality of lstat() system call, you can customize this to your wishes.
Get Last Modified Date of File in Linux
I’m new to Linux. I’m using the command-line. I’m trying to view the last modified date of a file. How do I do that in Linux from the Command Line?
@BrunoBieri It’s the modification date. See man ls . Typical Linux file systems don’t even track creation date — see the accepted answer for the kinds of dates kept track of.
7 Answers 7
As mentioned by @edvinas.me, stat tells you various information about the file including the last modified date.
At first, I was confused with Modify and Change, just to clarify, stat output lists:
- Access shows the time of last data access (e.g. read).
- Modify shows the time of last data modification.
- Change shows the time the file status last changed.
~ $ touch foo ~ $ stat foo File: ‘foo’ Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: fc01h/64513d Inode: 410397 Links: 1 Access: (0644/-rw-r--r--) Uid: (80972/ etomort) Gid: (18429/ eem_tw) Access: 2015-09-21 12:06:11.343616258 +0200 Modify: 2015-09-21 12:06:11.343616258 +0200 Change: 2015-09-21 12:06:11.343616258 +0200 Birth: - ~ $ echo "Added bar to foo file" >> foo ~ $ stat foo File: ‘foo’ Size: 42 Blocks: 8 IO Block: 4096 regular file Device: fc01h/64513d Inode: 410654 Links: 1 Access: (0644/-rw-r--r--) Uid: (80972/ etomort) Gid: (18429/ eem_tw) Access: 2015-09-21 12:09:31.298712951 +0200 Modify: 2015-09-21 12:09:31.298712951 +0200 Change: 2015-09-21 12:09:31.302713093 +0200 Birth: - ~ $ chmod 444 foo ~ $ stat foo File: ‘foo’ Size: 42 Blocks: 8 IO Block: 4096 regular file Device: fc01h/64513d Inode: 410654 Links: 1 Access: (0444/-r--r--r--) Uid: (80972/ etomort) Gid: (18429/ eem_tw) Access: 2015-09-21 12:09:31.298712951 +0200 Modify: 2015-09-21 12:09:31.298712951 +0200 Change: 2015-09-21 12:10:16.040310543 +0200 Birth: -
Use stat command for that:
Another way that is more flexible is using date -r . From man date :
-r, --reference=FILE display the last modification time of FILE
This has the advantage of allowing you to specify the output format, e.g.
$ date -r foo Thu Aug 31 10:36:28 AEST 2017 $ date -r foo -R Thu, 31 Aug 2017 10:36:28 +1000 $ date -r foo -u Thu Aug 31 00:36:28 UTC 2017 $ date -r foo +%s 1504139788
Yes, very helpful, thanks. Here is a bash function that will rename a file to be prefixed by the modified time: function mvfilestime() < if [ x"$<1>» = «x» ] ; then echo «mvfilestime: Missing argument of file to mv» else f=$(date +»%Y-%m-%d-%H-%M» -r $<1>)-$ <1>echo mv $ <1>$ mv $ <1>$ fi >1>
#> ls -l /home/TEST/ total 16 -rw-r--r-- 1 rfmas1 nms 949 Nov 16 12:21 create_nd_lists.py -rw-r--r-- 1 rfmas1 nms 0 Nov 16 12:35 enb_list -rw-r--r-- 1 rfmas1 nms 0 Nov 16 12:35 nb_list -rw-r--r-- 1 rfmas1 nms 0 Nov 16 12:35 nodes_ip.txt -rw-r--r-- 1 rfmas1 nms 0 Nov 16 12:35 rnc_list
Building off of @Adam Taylor ‘s comment in @phoops ‘s answer and @Sparhawk ‘s answer.
To specifically just get the date (using October 3, 2019 for examples because it was my last birthday)
- stat -c %y file | cut -d’ ‘ -f1 will give you 2019-10-03
- date +%F -r file will also give you 2019-10-03
- date +%D -r file will give you 10/03/19
- date +%x -r file will probably give either 10/03/2019 , or 10/03/19 if you’re in the U.S. and either 03/10/2019 , or 03/10/19 if you’re in the U.K., just to name a couple examples (of course there are more possibilities)
These date format options are, to my understanding, combinations of other format options. Here are some explanations from the man page:
%b locale’s abbreviated month name (e.g., Jan)
%B locale’s full month name (e.g., January)
.
%d day of month (e.g, 01)
%D date; same as %m/%d/%y
%e day of month, space padded; same as %_d
%F full date; same as %Y-%m-%d
.
%m month (01..12)
.
%x locale’s date representation (e.g., 12/31/99)
.
%y last two digits of year (00..99)
%Y year
.
By default, date pads numeric fields with zeroes.
The following optional flags may follow `%’:
— (hyphen) do not pad the field
_ (underscore) pad with spaces
0 (zero) pad with zeros
^ use upper case if possible
use opposite case if possible
N.B.: These flags don’t work on the «combo formats» like %F , %D and %x . They are for the «singular field formats«.
Apparently this last flag (#) does not work as I’d expect (e.g., if date +%b gives Oct , date +%#b gives OCT as opposed to oCT ) I guess this would be useless, but I’d think a lower case option would be more useful. date +%#p does turn date +%p which might give PM or AM into pm or am , respectively. So I guess it’s not a ‘per-character’ case switch but sets the case of all the characters in the string to the opposite case of the majority of the characters? Also date +%P gives pm or am , but neither date +%^P nor date +%#P change its output. My guess for this case is that %P is just an alias for %#p , and it seems that whenever you add more than one flag, the behavior is undefined/unpredictable ( e.g., date +%0-e gives the same as date +%-e : 3 and date +%-0e gives the same as date +%0e : 03 , which makes you think that only the flag next to the letter works or that it goes left to right, but both date +%#^p and date +%^#p give pm or am , [depending on the time of course] ) unless there’s some hidden order of operations? Sorry for digressing.
Also, if you run the command locale -k LC_TIME | grep ^d_fmt , you can see the combo for the specific locale of your system (e.g., d_fmt=»%m/%d/%Y» ).
And you can make your own combo. For example,