[net.unix-wizards] A Shell Procedure for Directory Structure Listing

res (01/25/83)

There have been several programs posted to the net to get a listing
of the structure of a directory system.  The following shell also
generates a simple form of such a listing, without all the grand
and glorious information about access privileges, size, et al.
I have called it  catalog  ... since it is recursive you should
name it likewise, or change the recursive invocation.  The instructions
are reasonably complete.  



# catalog - produce complete file structure list from directory
# 
# Parameters: 	1: directory name (optional)
# 		2: indentation string (empty on first call)
# 
# Produces on standard output the file structure emanating from
#   the current directory.  Each descent into a subdirectory
#    is indicated by further indentation.  Directories are indicated
#    by surrounding [], and executable files are prefaced with a *.
# 
if [ "$1" = "" ]
then
   echo "file structure from directory `pwd`"
   date
   echo "\n\n"
fi
if [ "$1" != "" ]
then
   cd $1
fi
for i in *
do
   if test -d $i
   then
	 echo "${2}[ $i ]"
	 catalog $i "$2  . "
   else
	 if [ "$i" != '*' ]
	 then
	    if test -x $i
	    then
	       echo "${2} *$i "
	    else
	       echo "${2}  $i "
	    fi
	 fi
   fi
done



Some sample output follows:



file structure from directory /n1/res/bin
Tue Jan 25 12:49:44 CST 1983



 *fort 
  fortdata 
 *sh 
[ source ]
  .   fort.c 
  .   sh.c 
  .   soxpnd.c 
 *soxpnd 
  temp 

nyuada (01/28/83)

Would someone please explain to me why everbody is writing shell scripts
that do recursive directory listings?  How are they significantly better
than "ls -R"?  If you want to do more general things than list files, but
do it recursively to a directory tree, you can use "find".  It's syntax
is a little odd and its semantics are kludgy so I wrote a csh script that
executes an arbitrary one-line csh command repeatedly (and, optionally,
recursively) on a list of files/directories.  It follows, beginning with
a comment containing the documentation.  Hope this is useful to some of
you.

#
#			 Loop on files
#
# Synopsis:
#		loopf [-R[ad]] cmd file...
#
# Executes cmd once for each file, arranging that $1 will be set  to  the  file
# name  during  execution  of  cmd.   $2,  $3, ... will be set to the remaining
# (unprocessed) args.  If no files are given, "." is used.   If  there  are  no
# occurrences  of  $  in the cmd, " $1" is appended to it causing the cmd to be
# executed with each file as a single parameter.  If -R is set, the cmd is  not
# executed  on  directory  files, but the names of such directories are printed
# and they are recursively processed.  With -Rd, the cmd is executed on  direc-
# tories  instead  of printing them, then they are recursively processed.  With
# -Ra or -Rad, files beginning with . (except . and ..) are processed also, but
# note that "loopf -Ra cmd *" will not process such files in the current direc-
# tory, because the * is expanded before loopf is invoked (use "loopf  -Ra  cmd
# .").   All  cmd executions (at same recursive level) get executed in a single
# csh process.  Hence csh variables may be set and tested usefully.   Also,  to
# control execution, break, exit and continue may be used within the cmd.
#
# Examples:
#	loopf 'od -X' *				/* prints hex dump of all
#						/* files in current dir */
# 	loopf 'cp $1 $1.back' a b		/* causes: cp a a.back */
#						/*	   cp b b.back */
# 	loopf 'echo $#argv ; break' *		/* prints # files in dir */
#	loopf 'if ($2 != "") mv $2 $1' tmp a b tmp
#						/* swaps files a and b */
#	loopf -R 'if ($1 =~ *.c) ls -l $1' /	/* lists all .c files */
#	loopf -Rd\
#	'echo $1; if (-d $1) mkdir dest/$1 ; if (! -d $1) cp $1 dest/$1' *
#						/* recursively copies current
#						   dir to dest dir, making any
#						   neccessary subdirs. */
#	loopf \
#	'if (! $?sum) @ sum=0 ; @ sum = $sum + $1 ; if ($2 == "") echo $sum' \
#		$vector				/* prints +/vector (plus */
#						/* reduction of $vector) */
#
set nonomatch
set recurse = "false"
set dodirs = "false"
set dodots = "false"
if ("$1" =~ -R*) then
#    echo "Recursive loop"
    set switch = "$1"
    set recurse = "true"
    if ("$1" =~ -R*d*) then
	set dodirs = "true"
    endif
    if ("$1" =~ -R*a*) then
	set dodots = "true"
    endif
    shift
endif
if ($#argv == 0) then
    echo "Usage: loopf [-R[ad]] cmd file..."
    exit
else if ($#argv == 1) then
    set argv = ("$argv[1]" ".")
endif
if ("$1" =~ *\$*) then
    set cmdarg = "$1"
else
    set cmdarg = "$1"' $1'
endif
alias cmd 'set argv=(\!*) ; '"$cmdarg"
set files = ($argv[2-]:q)
set needblnk = "false"
while ($#files > 0)
    if ($recurse == "true" && -d $files[1]) then
	if ($dodots == "true") then
	    set dircnts = ( {${files[1]}/.[0-z],${files[1]}/}* )
	else
	    set dircnts = ( ${files[1]}/* )
	endif
	if ("$dircnts" =~ *\*) set dircnts = ()
	if ($dodirs == "true") then
	    cmd $files
	else
	    if ($needblnk == "true") echo " "
	    echo "${files[1]}:"
	endif
	if ($#dircnts > 0) then
	    loopf "$switch" "$cmdarg" $dircnts:q
	    if ($dodirs == "false") echo " "
	    set needblnk = "false"
	endif
    else
	cmd $files
	set needblnk = "true"
    endif
    shift files
end