Barry Margolin <
bar...@alum.mit.edu> wrote:
> <
5f9d0ee3-7fcf-497c-93d1-20fa2e719...@r16g2000prr.googlegroups.com> wrote:
> bsh <
brian_hi...@rocketmail.com> wrote:
> > ...
> However, if the filename contains whitespace, that will mess up column
> counting.
... Including that darned "=>" token....
> > It's even a bit more complicated than that, insofar as the number of
> > ls(1) fields will vary if the create/access/modify time of the given
> > filename is newer than a certain internally defined period.
> No it doesn't. It's either "Mon Day Time" or "Month Day Year", but
> either way it's the same number of fields.
Yes, it does.
I was speaking to the algorithm (and fundamental issue) posed by the
OQ,
not pertaining to the algorithm used by resolvepath, which is not the
one
utilized in this function exactly because of this weakness, as
discussed.
Instead of directly rebuting your assertion, let me speak
metalogically:
how could I NOT be correct, insofar as I am the author of it, its era
of
authorship before Y2K and POSIX conformance, the addressed criterion
of portability between BSD and SysV versions of ls(1), and the
relative
ease of determining the historical validity of the stated behavior.
Like many of my function library, resolvepath was originally a bourne
shell function, and later ported to the more full featured ksh(1). I
wrote
another function, stat, than also was originally wrote in sh(1), and
was
called by that original resolvepath.sh. The above issue is documented
and worked around fully in that, including:
"Only when the file is less than six months old will the timestamp
(access-, create-, or mod-time) information be available to
calculate
a floating point remainder of a day when given the -r option."
#! /bin/echo error: only source
#*TAG:06847-11:1975-02-23:0644:stat:
# Date: 2003-01-04
# Description: shell emulation of stat(2)/lstat(2)
# License: copyright (c) 1998-2011
# Name: stat
# Project: @(#)stat.sh 1.10.4 20030104
b...@iname.com (Brian Hiles)
# Requires: [dc(1)], [expr(1)], ls(1), sed(1), [which(1)]
# See-also:
ftp://ftp.uu.net/usenet/comp.sources.misc/volume10/stat
# See-also:
ftp://ftp.uu.net/usenet/comp.sources.unix/volume18/fstat2
# See-also:
http://sunsite.unc.edu/pub/Linux/utils/scripts/binstats-1.00.tgz
# See-also: kstat.ksh <
jo...@armory.com> XXX
# Thanks-to: XXX <symbolic to numeric file permissions sed script>
# Usage: stat [-acfghlmrsv] file...
# Version: 1.10004
#XXX change -r option to five significant fractional digits
#XXX fails: -r option does not work with some cases: 123.0.123
#XXX to do: option to resolve full path of filename argument (for
mktag)
#XXX to do: convert "test -h" into case $... in *' -> '*) for
portability
#XXX to do: reduce extraneous calls to ls(1)
#XXX to do: new variable _link; only one while loop ls(1) ?? #XXX@
#XXX to do: how does this respond to a symlink infinite loop?
#XXX fails: stat my-symlink => _type=l but should be _type=f
#XXX to do: turn -f option into (-f num) to iterate thru num symlinks
(<=256)
#01
stat() # [-acfghlmrsv] file...
# emulate stat(2)
{ # variable side-effects: _blocks _ino _gid _mode _name _perms
# _rdev _size _time _type _uid
_day= _fall= _follow=ON _fptime= _group=ON _headers= _o= \
_pivot='[0-6][0-9]' _rc= _tab=' ' _tmopt=t _usage=\
'usage: stat [-acfghlmrsv] file...
-a - show last access time
-c - show last inode change time
-f - follow all symlinks [one only]
-g - give -g option to ls command
-h - prepend argument headers [default for multiple arguments]
-l - show stat on symlink itself
-m - show last modification time [default]
-p - perform a path lookup on the file argument
-r - show time as a floating point number, if possible
-s - do not output, only set variables
-v - verbose output'
while getopts :acFfGgHhLlmPpRrSsVvy: _inst
do case $_inst in
a) _tmopt=u ;;
c) _tmopt=c ;;
f) _fall=ON ;; # follow all symlink chains?
+f|F) _fall= ;; # follow symlink once only
g) _group=ON ;; # "ls -g" includes group info?
+g|G) _group= ;;
h) _headers=ON ;;
+h|H) _headers=OFF ;;
l) _follow= ;;
+l|L) _follow=ON ;;
m) _tmopt=t ;;
p) _pathlu=ON ;;
+p|P) _pathlu= ;;
r) _fptime=ON ;;
+r|R) _fptime= ;;
s) _silent=ON ;;
+s|S) _silent= ;;
v) _o=ON ;;
+v|V) _o= ;;
y) _pivot=$OPTARG ;; # undocumented
[?:]) echo "$_usage" >&2
return 2 ;;
esac
done
set X ${1+"$@"}; shift $OPTIND # do: shift `expr 0$OPTIND - 1`
if test X$_headers = XOFF
then _headers=
else test 0$# -gt 1 && _headers=ON
fi
_Jan=1 _Feb=2 _Mar=3 _Apr=4 _May=5 _Jun=6 _Jul=7 _Aug=8 \
_Sep=9 _Oct=10 _Nov=11 _Dec=12 _oifs=$IFS _opath=$PATH
IFS=' ' PATH=/bin${PATH:+:$PATH} # SunOS 4.x: use /usr/5bin/ls
# find current date having four digit year
eval ` date '+_curry=%Y _currm=%m _currd=%d' 2>/dev/null ||
date '+_curry=%y _currm=%m _currd=%d'
`
eval "case \$_curry in # default '69 pivot year
????) ;;
$_pivot)_curry=20\$_curry ;;
??) _curry=19\$_curry ;;
esac"
for _inst in "$@"
do _origarg=$_inst
test X$_pathlu = XON &&
if set -- ` which $_inst 2>&1 ` # SunOS which(1) rc bug
case $1 in
"no $1 in "*)
echo >&2 \
"stat: error: cannot find \"$_inst\" in path"
continue ;;
/*) _inst=$1
test -f $_inst ;;
esac
then :
else echo error: $_inst: not in path >&2
_rc="X $_rc" continue
fi
set -- X "$_inst"; shift
_blocks= _fraction= _gid= _ino= _mode= \
_name= _rdev= _size= _time= _type= _uid=
test X$_follow = XON &&
while (test -L "$1" 2>/dev/null || test -h "$1")
do set X ` BLOCKSIZE=512 LANG=C LC_ALL=C LC_TIME=C \
\ls -dl -- "$1" 2>&-
`; shift
eval "${DIAG:+echo >/dev/tty 'symlink-indirection: \"$1\"'}"
# workaround for SunOS ls(1) bug: returns 0 on EACCESS!
test 0$# -eq 0 && _rc="X $_rc" continue 2
eval "set X \${1+\"\$@\"}; shift $#"
test X$_fall = X && break # one level only?
done
# $1 now is the [symlink indirection] of argument
if set X ` BLOCKSIZE=512 LANG=C LC_ALL=C LC_TIME=C \
\ls -idls$_tmopt${_group:+g} -- "$1" 2>&-
`; shift
# SunOS ls(1) rc bug: returns 0 on EACCESS!
test 0$# -ne 0
then _ino=$1 _blocks=$2 _mode=$3 _perms=$3 _nlinks=$4 \
_uid=$5 shift 5
else echo "stat: error: cannot stat \"$_origarg\"" >&2
_rc="X $_rc" continue
fi
# set variables _mode, _perms, and _type:
eval ` echo "$_perms" | sed \
-e 's/.\(..\(.\)..\(.\)..\(.\)\).*/\2\3\4\1/
h; s/[a-z]/9/g; x; s/[STst]/9/g; G
s/\(...\).*\n...\(.*\)/\1\2/; s/[^9]/8/g
: loop
s/$/;88808891898289939884989599869997/
s/^\([0-7]*\)\(...\)\(.*\);.*\2\(.\).*/\1\4\3/
/;/!b loop' \
-e "s/;.*//; s/^/_perms=$_perms _mode=/
s/^_perms=\\(.\\)/_type=\\1 _perms=/
s/^_type=- /_type=f /"
`
# is the group field included in the ls(1) output?
case $#$_type in [79][bc]|[68][!bc]) _gid=$1 shift ;; esac
case $_type in # block or char special device?
[bc]) _size=0 _rdev=$1$2 shift 2 ;;
*) _size=$1 shift ;;
esac
case $3 in # which field: year or time?
*:*) _year=$_curry
test X$_fptime = XON && _fraction=`
IFS=:
set -- $3
test 0$# -eq 2 &&
# Bug! 0.12345 needs the leading zero truncated
echo "5k$1 60*$2+1440/ps." | dc
` ;;
*) _year=$3 ;;
esac
eval _month=\$_$1 _day=\$2 # convert month name to number
case $_month in ?) _month=0$_month; esac
# if filemonth > currentmonth then it must be for previous year
test 0$_month -gt 0$_currm && _year=` expr $_year - 1 `
case $_day in ?) _day=0$_day; esac
_time=$_year-$_month-$_day$_fraction shift 3 #XXX 0.12345
if test "X$2" = 'X->'
then _name=$1
else _name=$* # _attempt_ filenames w/ spaces
fi
# quote filenames with embedded operators or globbing-chars
# an embedded single quote will still break the output
case $_name$_origarg in *['"$&()*;<>?[|']*)_q=\';;*)_q=;;esac
test "X$_silent" = X &&
echo "${_headers:+$_q$_origarg$_q:$_tab}${_o:+type=}$_type" \
"${_o:+inode=}$_ino ${_o:+mode=}$_mode ${_o:+nlinks=}$_nlinks" \
"${_o:+uid=}$_uid ${_o:+gid=}$_gid ${_o:+size=}$_size" \
"${_o:+blocks=}$_blocks ${_o:+time=}$_time" \
"${_rdev:+ ${_o:+rdev=}$_rdev}${_o:+name=}$_q$_name$_q" # <=TAB
done
set X $_rc; shift # retval is num of args in err
unset _Jan _Feb _Mar _Apr _May _Jun _Jul _Aug _Sep _Oct _Nov _Dec
unset _day _fall _follow _fptime _fraction _group _headers _ls _month
unset _o _origarg _q _rc _silent _tab _tmopt _usage _year #_inst
eval "unset _oifs _opath; IFS='$_oifs' PATH='$_opath' return $#"
}
#02 EMBEDDED MANPAGE FOR "src2man"
: '
!++
NAME
stat -- shell emulation of stat(3)/lstat(3)
SYNOPSIS
stat [-acfghlmrsv] file...
OPTIONS
-a - show last access time.
-c - show last inode change time.
-f - follow all symlinks [one only].
-g - give -g option to ls command.
-h - prepend argument headers [default for multiple arguments].
-l - show stat on symlink itself.
-m - show last modification time [default].
-r - show time as a floating point number, if possible.
-s - do not output, only set variables.
-v - verbose output.
DESCRIPTION
Function stat(3S) emulates stat(3) and lstat(3). The -l option will
stat the inode which represents the symbolic link, just as lstat(3)
does. The -a, -c, and -m options show the access, inode change/
create,
and modification times, respectively.
Unlike stat(3), additional options are available as befitting the
high-level nature of shell: the -f option is a directive to follow
all symbolic links, if there are multiple indirections; the -r option
may be used to allow the three times to be in the form of a floating
point number.
Use option -h to necessarily prepend headers of the original
parameter;
option -v will output in the form of a variable assignment list.
Conversely, use option -s to only set, not display, the variables
_blocks, _gid, _ino, _mode, _nlinks, _rdev, _size, _time, and _uid
(corresponding to the structure "stat" fields declared in <sys/
stat.h>)
as well as _name, _perms, and _type variables. _type takes the values
[fbcdlps] to mean the argument is a plain file, block-special file,
character-special file, directory, symbolic link, named pipe, or
socket. _blocks is expressed in multiples of 512 bytes, and as such
does not technically reflect the st_size field in structure stat(3)
because the block-size for the file-system of that file may be a
different value. Variable _perms holds the symbolic permissions field
as accepted by chmod(1). There is no analogue for the stat(3) field
st_dev.
Read the CAVEATS section for a discussion for the necessity of the
-g option.
RETURN VALUE
Returns 0 if successful, 2 for options parsing errors, otherwise
the number of arguments in error.
AUTHOR
Brian Hiles <
b...@iname.com>
SEE ALSO
resolvepath(3S)
NOTES
Date output format conforms to ISO-8601. #XXX
This function makes side-effects upon the variables _blocks, _ino,
_gid, _mode, _name, _perms, _rdev, _size, _time, _type, and _uid.
In sh(1), a handy idiom to do word splitting upon (for example)
$_time, given a character (for example) "-", is:
eval "IFS=-; set -- \$_time; IFS='$IFS'"
$1 will now hold the year, $2 the month, and $3 the day. This can
also be used for separating directory components in a path. Variable
IFS (which cannot contain a single-quote) will not be modified.
A general method to do field-splitting upon the output of stat(3S)
is:
eval ` stat -v file `
But it is more efficient to use the -s option and directly use the
side-effect variables listed above.
CAVEATS
The output format of the ls(1) command is different, sometimes for
the same option, between the System V and BSD derived
implementations.
Regarding the inclusion of a -g option, for BSD ls(1) the group field
is printed, otherwise it not; for System V ls(1), the group field
replaces the owner field, otherwise both are printed. There is no
robust and portable method of determining the difference: either the
-g or -G option to stat(3S) may need to be specified or not depending
on either behavior.
The name and group fields may be either a name or a number; it
depends
how the underlying ls(1) resolves uid and/or gid information.
With the -r option, the precision of the floating-point date/time
number is better than for a granularity of one second; however, the
granularity of the ls(1) time field is to the nearest minute. XXX
BUGS
Only when the file is less than six months old will the timestamp
(access-, create-, or mod-time) information be available to calculate
a floating point remainder of a day when given the -r option.
This function is broken by filenames with leading/following and/or
two or more sequential embedded space characters.
Limitations and caveats for the stat(3S) function are the same as
for ls(1), as this function is effectively a parser built over it.
Too heavyweight: ls(1) is executed at least twice for each file
argument, and additionally up to 257 times to potentially resolve
a symlink!
!--
'