chip@chinacat.Unicom.COM (Chip Rosenthal) (07/31/90)
Attached at the bottom is a command-line interface to the stat() system call. To do the file compare, you could do: time1=`stat -M $file1` time2=`stat -M $file2` if [ $time1 -gt $time2 ] ; then echo "$file1 is newer than $file2" else echo "$file2 is newer than $file1" # well...could be same time... fi In article <1990Jul27.153223.12416@chinet.chi.il.us> les@chinet.chi.il.us (Leslie Mikesell) writes: >Also, is there a handy way to set the timestamp on a directory to that >of the newest file below it? Assuming you've got a "settime" command, this would work: DIR=/tmp/foo settime -f `stat -M $DIR/* | sort -rn +1 | sed -e 's/:.*//' -e q` $DIR This sets the directory's modified time to that of the newest file in that directory. A little more complexity is needed if you want to find the newest file in the entire directory tree. The stat program has been tested on SCO XENIX 386. I wouldn't be surprised if there are some system dependancies in the include files or data types of the stat structure elements, but it should be very simple to fix up. (The whole thing is driven by a single table.) #! /bin/sh # this is a "shar" archive - run through "/bin/sh" to extract 4 files: # README stat.c stat.man Makefile # Wrapped by bin@chinacat on Sun Jul 29 20:19:14 CDT 1990 # Unpacking this archive requires: sed test wc (possibly mkdir) # Existing files will not be clobbered unless "-c" is specified on the cmd line. if test -f README -a "$1" != "-c" ; then echo "README: file exists - will not be overwritten" else echo "x - README (file 1 of 4, 586 chars)" sed -e 's/^X//' << 'END_OF_FILE_README' > README Xstat displays information on a file - the same information available Xthrough the stat() system call. For example, to see all the information Xon /etc/termcap you can do: X X % stat -x /etc/termcap X DEV=1,40 X INODE=3087 X MODE=644 X NLINK=4 X UID=bin X GID=bin X RDEV=103,181 X SIZE=94032 X ATIME=Sun Jul 29 18:36:18 1990 X MTIME=Sun Apr 30 22:53:31 1989 X CTIME=Sun Nov 5 02:23:55 1989 X XThe "-x" option returns all available things. You can request individual Xitems, for example: X X % stat -u /etc/termcap X bin X XChip Rosenthal X<chip@chinacat.Unicom.COM> END_OF_FILE_README size="`wc -c < README`" if test 586 -ne "$size" ; then echo "README: extraction error - got $size chars" fi fi if test -f stat.c -a "$1" != "-c" ; then echo "stat.c: file exists - will not be overwritten" else echo "x - stat.c (file 2 of 4, 7223 chars)" sed -e 's/^X//' << 'END_OF_FILE_stat.c' > stat.c X/* X * stat - User-level interface to file information. X * X * This program is table driven through the "Stat_tab[]" list, which contains X * all of the items we can display. Each table entry specifies the command X * line option which selects it, a pointer to pick the appropriate value out X * of a stat structure, and a pointer to the procedure to format and print the X * information. Hacking this program should just be a matter of adding table X * entries, and possibly new formatting procedures. X * X * Sun Jul 29 20:16:50 1990 - Chip Rosenthal <chip@chinacat.Unicom.COM> X * Commented for posting to alt.sources. X */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/sysmacros.h> X#include <pwd.h> X#include <grp.h> X X/* X * Used to mark what kind of data item we are looking at. X */ X#define TYPE_DEV_T 1 X#define TYPE_INO_T 2 X#define TYPE_OFF_T 3 X#define TYPE_SHORT 4 X#define TYPE_TIME_T 5 X#define TYPE_USHORT 6 X X/* X * The following procedures take a "long" value and format it as required. X */ Xvoid pr_decvalue(); /* print as a decimal value */ Xvoid pr_octvalue(); /* print as an octal value */ Xvoid pr_perms(); /* strip and print permissions in octal */ Xvoid pr_device(); /* print device number as "major,minor" */ Xvoid pr_user(); /* lookup and print as a user name */ Xvoid pr_group(); /* lookup and print as a group name */ Xvoid pr_time(); /* print as a formatted time string */ Xvoid pr_days(); /* print as number of days ago */ X X/* X * The file status will be loaded into this buffer. X */ Xstruct stat Stat_buf; X X/* X * Table of things we can do. X */ Xstruct stat_desc { X int sd_enabled; /* will be set nonzero to do this entry */ X char *sd_optlist; /* list of cmd line options which enable this */ X char *sd_descrip; /* text for item label */ X void *sd_ptr; /* pointer to item datum within "Stat_buf" */ X int sd_datatype; /* indicates what "sd_ptr" points to */ X void (*sd_func)(); /* procedure to format and print the item */ X} Stat_tab[] = { X /*** lower case options ************************************************/ X { 0, "dx", "DEV", (void*)&Stat_buf.st_dev, TYPE_DEV_T, pr_device }, X { 0, "ix", "INODE", (void*)&Stat_buf.st_ino, TYPE_INO_T, pr_decvalue }, X { 0, "px", "MODE", (void*)&Stat_buf.st_mode, TYPE_USHORT, pr_perms }, X { 0, "nx", "NLINK", (void*)&Stat_buf.st_nlink, TYPE_SHORT, pr_decvalue }, X { 0, "ux", "UID", (void*)&Stat_buf.st_uid, TYPE_USHORT, pr_user }, X { 0, "gx", "GID", (void*)&Stat_buf.st_gid, TYPE_USHORT, pr_group }, X { 0, "rx", "RDEV", (void*)&Stat_buf.st_rdev, TYPE_DEV_T, pr_device }, X { 0, "sx", "SIZE", (void*)&Stat_buf.st_size, TYPE_OFF_T, pr_decvalue }, X { 0, "ax", "ATIME", (void*)&Stat_buf.st_atime, TYPE_TIME_T, pr_time }, X { 0, "mx", "MTIME", (void*)&Stat_buf.st_mtime, TYPE_TIME_T, pr_time }, X { 0, "cx", "CTIME", (void*)&Stat_buf.st_ctime, TYPE_TIME_T, pr_time }, X { 0, "f", "MTIME", (void*)&Stat_buf.st_mtime, TYPE_TIME_T, pr_days }, X /*** upper case options ************************************************/ X { 0, "DX", "DEV", (void*)&Stat_buf.st_dev, TYPE_DEV_T, pr_decvalue }, X { 0, "IX", "INODE", (void*)&Stat_buf.st_ino, TYPE_INO_T, pr_decvalue }, X { 0, "PX", "MODE", (void*)&Stat_buf.st_mode, TYPE_USHORT, pr_octvalue }, X { 0, "NX", "NLINK", (void*)&Stat_buf.st_nlink, TYPE_SHORT, pr_decvalue }, X { 0, "UX", "UID", (void*)&Stat_buf.st_uid, TYPE_USHORT, pr_decvalue }, X { 0, "GX", "GID", (void*)&Stat_buf.st_gid, TYPE_USHORT, pr_decvalue }, X { 0, "RX", "RDEV", (void*)&Stat_buf.st_rdev, TYPE_DEV_T, pr_decvalue }, X { 0, "SX", "SIZE", (void*)&Stat_buf.st_size, TYPE_OFF_T, pr_decvalue }, X { 0, "AX", "ATIME", (void*)&Stat_buf.st_atime, TYPE_TIME_T, pr_decvalue }, X { 0, "MX", "MTIME", (void*)&Stat_buf.st_mtime, TYPE_TIME_T, pr_decvalue }, X { 0, "CX", "CTIME", (void*)&Stat_buf.st_ctime, TYPE_TIME_T, pr_decvalue }, X { 0, NULL, NULL, (void*)0, 0, 0 }, X}; X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X char *s; X int print_titles, print_files, argi, ocount, i; X long value; X char *strchr(); X X /* X * Crack the command line options. X */ X for ( argi = 1 ; argi < argc && *(s=argv[argi]) == '-' ; ++argi ) { X while ( *++s != '\0' ) { X ocount = 0; X for ( i = 0 ; Stat_tab[i].sd_optlist != NULL ; ++i ) { X if ( strchr(Stat_tab[i].sd_optlist,*s) != NULL ) { X ++Stat_tab[i].sd_enabled; X ++ocount; X } X } X if ( ocount == 0 ) { X fprintf(stderr,"%s: unknown option '%c'\n",argv[0],*s); X exit(1); X } X } X } X X /* X * If more than one item is printed, then titles should be shown. X * Note that if you double an option (i.e. "-dd") this will cause titles X * to be printed. Consider it an undocumented feature... X */ X switch ( ocount ) { X case 0: fprintf(stderr,"%s: no options specified\n",argv[0]); exit(1); X case 1: print_titles = 0; break; X default: print_titles = 1; break; X } X X /* X * If more than one file is to be done, then names should be shown. X */ X switch ( argc-argi ) { X case 0: fprintf(stderr,"%s: no files specified\n",argv[0]); exit(1); X case 1: print_files = 0; break; X default: print_files = 1; break; X } X X /* X * Go through the remaining command line items and get their status. X */ X for ( ; argi < argc ; ++argi ) { X X if ( stat(argv[argi],&Stat_buf) != 0 ) { X fprintf(stderr,"%s: could not get file status of '%s'\n", X argv[0],argv[argi]); X continue; X } X X for ( i = 0 ; Stat_tab[i].sd_optlist != NULL ; ++i ) { X if ( !Stat_tab[i].sd_enabled ) X continue; X switch ( Stat_tab[i].sd_datatype ) { X case TYPE_DEV_T: value = *((dev_t *)Stat_tab[i].sd_ptr); break; X case TYPE_INO_T: value = *((ino_t *)Stat_tab[i].sd_ptr); break; X case TYPE_OFF_T: value = *((off_t *)Stat_tab[i].sd_ptr); break; X case TYPE_SHORT: value = *((short *)Stat_tab[i].sd_ptr); break; X case TYPE_TIME_T: value = *((time_t *)Stat_tab[i].sd_ptr); break; X case TYPE_USHORT: value = *((ushort *)Stat_tab[i].sd_ptr); break; X } X if ( print_files ) X printf("%s: ", argv[argi]); X if ( print_titles ) X printf("%s=", Stat_tab[i].sd_descrip); X (*Stat_tab[i].sd_func)(value); X } X X } X X exit(0); X} X X Xvoid pr_decvalue(value) Xlong value; X{ X printf("%ld\n", value); X} X Xvoid pr_octvalue(value) Xlong value; X{ X printf("%lo\n",value); X} X Xvoid pr_perms(value) Xlong value; X{ X printf("%lo\n", (value&~S_IFMT) ); X} X Xvoid pr_device(value) Xlong value; X{ X printf("%ld,%ld\n", major(value), minor(value)); X} X Xvoid pr_user(value) Xlong value; X{ X struct passwd *pw, *getpwuid(); X if ( (pw=getpwuid((int)value)) == NULL ) X printf("%ld\n", value); X else X puts(pw->pw_name); X} X Xvoid pr_group(value) Xlong value; X{ X struct group *gr, *getgrgid(); X if ( (gr=getgrgid((int)value)) == NULL ) X printf("%ld\n", value); X else X puts(gr->gr_name); X} X Xvoid pr_time(value) Xlong value; X{ X char *ctime(); X fputs(ctime(&value),stdout); X} X Xvoid pr_days(value) Xlong value; X{ X time_t clock, time(); X (void) time(&clock); X printf("%ld\n", ((long)clock-value)/86400L); X} X END_OF_FILE_stat.c size="`wc -c < stat.c`" if test 7223 -ne "$size" ; then echo "stat.c: extraction error - got $size chars" fi fi if test -f stat.man -a "$1" != "-c" ; then echo "stat.man: file exists - will not be overwritten" else echo "x - stat.man (file 3 of 4, 2468 chars)" sed -e 's/^X//' << 'END_OF_FILE_stat.man' > stat.man X.TH STAT 1L X.SH NAME Xstat - Displays file status. X.SH SYNTAX X.B stat X-options file ... X.SH DESCRIPTION XThe X.I stat Xcommand obtains information on a file. A plethora of options select which Xitems to display. Most items have two alternatives: a lower case letter Xwhich displays the item in a useful format, and an upper case letter which Xdisplays the item in a numeric format. Programmers will recognize these Xitems as the values returned by the X.I stat(2) Xsystem call. The available options are: X.sp X.nf X.ta \n(.iuC +8 +4n +22n +4n X.de XX X \f2\\$1\fP \f3\\$2\fP \\$3 \f3\\$4\fP \\$5 X.. X.XX dev -d "device major,minor" -D "decimal value" X.XX inode -i "inode number number" -I "(same information)" X.XX mode -p "permissions in octal" -P "full mode in octal" X.XX nlink -n "number of links" -N "(same information)" X.XX uid -u "name of owning user" -U "number of owning user" X.XX gid -g "name of owning group" -G "number of owning group" X.XX rdev -r "device major,minor" -R "decimal value" X.XX size -s "size in bytes" -S "(same information)" X.XX atime -a "last access time" -A "seconds since epoch" X.XX mtime -m "last modify time" -M "seconds since epoch" X.XX ctime -c "last change time" -C "seconds since epoch" X.XX "" -x "all above items" -X "all above items" X.XX mtime -f "days ago" "" "" X.fi X.P XPlease see the X.I stat(2) Xmanual page for the details on the items as listed in column one above. X.P XIf X.I stat Xis invoked for a single piece of information on one file, just that Xitem is printed. If more than one piece of information is requested, Xit will be labelled, for example: X.RS X.sp XUID=root X.sp X.RE XThe labels are derived from the column 1 names, with the "st" prefix Xdropped and the result converted to upper case. If information on Xmore than one file is requested, the filename will precede each line of output. X.P XNote there are three special options: X.B "-x" Xand X.B "-X" Xwhich request a number of options to be used, and the X.B "-f" Xoption which requests the modify time in a special format. The difference Xbetween the lower-case time options and upper-case time options is that Xthe former print the date as text, and the latter print the date in number Xof seconds since the ``epoch''. The lower-case device options print the Xdevice major and minor numbers seperated by commas, the upper-case Xdevice options show a single decimal value. X X X.SH SEE ALSO Xchgrp(1), chmod(1), chown(1), ls(1), stat(2) X.SH AUTHOR XChip Rosenthal X.br X<chip@chinacat.unicom.com> END_OF_FILE_stat.man size="`wc -c < stat.man`" if test 2468 -ne "$size" ; then echo "stat.man: extraction error - got $size chars" fi fi if test -f Makefile -a "$1" != "-c" ; then echo "Makefile: file exists - will not be overwritten" else echo "x - Makefile (file 4 of 4, 1260 chars)" sed -e 's/^X//' << 'END_OF_FILE_Makefile' > Makefile X X# %Z% %M% %I% %E% %U% X# Makefile for "stat" (generated by makemake version 1.00.09) X# Created by bin@chinacat on Sun Jul 29 20:15:19 CDT 1990 X XSHELL = /bin/sh XCC = cc XDEFS = XCOPTS = -O XLOPTS = XLIBS = XDEBUG = -g -DDEBUG XLINTFLAGS = -DLINT X XTARG = stat XOTHERS = X XSRCS = stat.c X XOBJS = stat.o X X# Any edits below this line will be lost if "makemake" is rerun! X# Commands may be inserted after the '#%custom' line at the end of this file. X XCFLAGS = $(COPTS) $(DEFS) # $(DEBUG) XLFLAGS = $(LOPTS) # $(DEBUG) X Xall: $(TARG) $(OTHERS) Xinstall: all ; inst Install Xclean: ; rm -f $(TARG) $(OBJS) a.out core $(TARG).lint Xclobber: clean ; inst -u Install Xlint: $(TARG).lint X X$(TARG): $(OBJS) X $(CC) $(LFLAGS) -o $@ $(OBJS) $(LIBS) X X$(TARG).lint: $(TARG) X lint $(LINTFLAGS) $(DEFS) $(SRCS) $(LIBS) > $@ X Xstat.o: stat.c X Xmake: ; X makemake -i -v1.00.09 -aMakefile \ X -DSHELL='$(SHELL)' -DCC='$(CC)' -DDEFS='$(DEFS)' \ X -DCOPTS='$(COPTS)' -DLOPTS='$(LOPTS)' -DLIBS='$(LIBS)' \ X -DDEBUG='$(DEBUG)' -DLINTFLAGS='$(LINTFLAGS)' \ X -DOTHERS='$(OTHERS)' $(TARG) $(SRCS) X X#%custom - commands below this line will be maintained if 'makemake' is rerun X Xshar: stat.shar X XLIST = README stat.c stat.man Makefile X Xstat.shar: $(LIST) X shar $(LIST) > $@ X END_OF_FILE_Makefile size="`wc -c < Makefile`" if test 1260 -ne "$size" ; then echo "Makefile: extraction error - got $size chars" fi fi echo "done - 4 files extracted" exit 0 -- Chip Rosenthal | You aren't some icon carved out chip@chinacat.Unicom.COM | of soap, sent down here to clean Unicom Systems Development, 512-482-8260 | up my reputation. -John Hiatt
fritz@mercury.caltech.edu (Fritz Nordby) (08/03/90)
Am I missing something here, or will the following provide the desired function: #!/bin/sh case $# in 2);;*)echo "usage: ${0:-newer} file1 file2" >&2;exit 2;;esac [ ! -f "$2" -o -f "$1" -a "X`/bin/ls -t \"\$1\" \"\$2\"`" = "X$1 $2"] >/dev/null 2>&1 I suppose this will fail if, for example, it is invoked as $ newer 'foo foo' 'foo' but people who put newlines in their filenames get what they deserve!