Пример A-21. Directory information

Пример A-21. Directory information

  1. #! /bin/bash
  2. # directory-info.sh
  3. # Parses and lists directory information.
  4. # NOTE: Change lines 273 and 353 per "README" file.
  5. # Michael Zick is the author of this script.
  6. # Used here with his permission.
  7. # Controls
  8. # If overridden by command arguments, they must be in the order:
  9. #   Arg1: "Descriptor Directory"
  10. #   Arg2: "Exclude Paths"
  11. #   Arg3: "Exclude Directories"
  12. #
  13. # Environment Settings override Defaults.
  14. # Command arguments override Environment Settings.
  15. # Default location for content addressed file descriptors.
  16. MD5UCFS=${1:-${MD5UCFS:-'/tmpfs/ucfs'}}
  17. # Directory paths never to list or enter
  18. declare -a \
  19.   EXCLUDE_PATHS=${2:-${EXCLUDE_PATHS:-'(/proc /dev /devfs /tmpfs)'}}
  20. # Directories never to list or enter
  21. declare -a \
  22.   EXCLUDE_DIRS=${3:-${EXCLUDE_DIRS:-'(ucfs lost+found tmp wtmp)'}}
  23. # Files never to list or enter
  24. declare -a \
  25.   EXCLUDE_FILES=${3:-${EXCLUDE_FILES:-'(core "Name with Spaces")'}}
  26. # Here document used as a comment block.
  27. : << LSfieldsDoc
  28. # # # # # List Filesystem Directory Information # # # # #
  29. #
  30. #       ListDirectory "FileGlob" "Field-Array-Name"
  31. # or
  32. #       ListDirectory -of "FileGlob" "Field-Array-Filename"
  33. #       '-of' meaning 'output to filename'
  34. # # # # #
  35. String format description based on: ls (GNU fileutils) version 4.0.36
  36. Produces a line (or more) formatted:
  37. inode permissions hard-links owner group ...
  38. 32736 -rw-------    1 mszick   mszick
  39. size    day month date hh:mm:ss year path
  40. 2756608 Sun Apr 20 08:53:06 2003 /home/mszick/core
  41. Unless it is formatted:
  42. inode permissions hard-links owner group ...
  43. 266705 crw-rw----    1    root  uucp
  44. major minor day month date hh:mm:ss year path
  45. 4,  68 Sun Apr 20 09:27:33 2003 /dev/ttyS4
  46. NOTE: that pesky comma after the major number
  47. NOTE: the 'path' may be multiple fields:
  48. /home/mszick/core
  49. /proc/982/fd/0 -> /dev/null
  50. /proc/982/fd/1 -> /home/mszick/.xsession-errors
  51. /proc/982/fd/13 -> /tmp/tmpfZVVOCs (deleted)
  52. /proc/982/fd/7 -> /tmp/kde-mszick/ksycoca
  53. /proc/982/fd/8 -> socket:[11586]
  54. /proc/982/fd/9 -> pipe:[11588]
  55. If that isn't enough to keep your parser guessing,
  56. either or both of the path components may be relative:
  57. ../Built-Shared -> Built-Static
  58. ../linux-2.4.20.tar.bz2 -> ../../../SRCS/linux-2.4.20.tar.bz2
  59. The first character of the 11 (10?) character permissions field:
  60. 's' Socket
  61. 'd' Directory
  62. 'b' Block device
  63. 'c' Character device
  64. 'l' Symbolic link
  65. NOTE: Hard links not marked - test for identical inode numbers
  66. on identical filesystems.
  67. All information about hard linked files are shared, except
  68. for the names and the name's location in the directory system.
  69. NOTE: A "Hard link" is known as a "File Alias" on some systems.
  70. '-' An undistingushed file
  71. Followed by three groups of letters for: User, Group, Others
  72. Character 1: '-' Not readable; 'r' Readable
  73. Character 2: '-' Not writable; 'w' Writable
  74. Character 3, User and Group: Combined execute and special
  75. '-' Not Executable, Not Special
  76. 'x' Executable, Not Special
  77. 's' Executable, Special
  78. 'S' Not Executable, Special
  79. Character 3, Others: Combined execute and sticky (tacky?)
  80. '-' Not Executable, Not Tacky
  81. 'x' Executable, Not Tacky
  82. 't' Executable, Tacky
  83. 'T' Not Executable, Tacky
  84. Followed by an access indicator
  85. Haven't tested this one, it may be the eleventh character
  86. or it may generate another field
  87. ' ' No alternate access
  88. '+' Alternate access
  89. LSfieldsDoc
  90. ListDirectory()
  91. {
  92.         local -a T
  93.         local -i of=0           # Default return in variable
  94. #       OLD_IFS=$IFS            # Using BASH default ' \t\n'
  95.         case "$#" in
  96.         3)      case "$1" in
  97.                 -of)    of=1 ; shift ;;
  98.                  * )    return 1 ;;
  99.                 esac ;;
  100.         2)      : ;;            # Poor man's "continue"
  101.         *)      return 1 ;;
  102.         esac
  103.         # NOTE: the (ls) command is NOT quoted (")
  104.         T=( $(ls --inode --ignore-backups --almost-all --directory \
  105.         --full-time --color=none --time=status --sort=none \
  106.         --format=long $1) )
  107.         case $of in
  108.         # Assign T back to the array whose name was passed as $2
  109.                 0) eval $2=\( \"\$\{T\[@\]\}\" \) ;;
  110.         # Write T into filename passed as $2
  111.                 1) echo "${T[@]}" > "$2" ;;
  112.         esac
  113.         return 0
  114.    }
  115. # # # # # Is that string a legal number? # # # # #
  116. #
  117. #       IsNumber "Var"
  118. # # # # # There has to be a better way, sigh...
  119. IsNumber()
  120. {
  121.         local -i int
  122.         if [ $# -eq 0 ]
  123.         then
  124.                 return 1
  125.         else
  126.                 (let int=$1)  2>/dev/null
  127.                 return $?       # Exit status of the let thread
  128.         fi
  129. }
  130. # # # # # Index Filesystem Directory Information # # # # #
  131. #
  132. #       IndexList "Field-Array-Name" "Index-Array-Name"
  133. # or
  134. #       IndexList -if Field-Array-Filename Index-Array-Name
  135. #       IndexList -of Field-Array-Name Index-Array-Filename
  136. #       IndexList -if -of Field-Array-Filename Index-Array-Filename
  137. # # # # #
  138. : << IndexListDoc
  139. Walk an array of directory fields produced by ListDirectory
  140. Having suppressed the line breaks in an otherwise line oriented
  141. report, build an index to the array element which starts each line.
  142. Each line gets two index entries, the first element of each line
  143. (inode) and the element that holds the pathname of the file.
  144. The first index entry pair (Line-Number==0) are informational:
  145. Index-Array-Name[0] : Number of "Lines" indexed
  146. Index-Array-Name[1] : "Current Line" pointer into Index-Array-Name
  147. The following index pairs (if any) hold element indexes into
  148. the Field-Array-Name per:
  149. Index-Array-Name[Line-Number * 2] : The "inode" field element.
  150. NOTE: This distance may be either +11 or +12 elements.
  151. Index-Array-Name[(Line-Number * 2) + 1] : The "pathname" element.
  152. NOTE: This distance may be a variable number of elements.
  153. Next line index pair for Line-Number+1.
  154. IndexListDoc
  155. IndexList()
  156. {
  157.         local -a LIST                   # Local of listname passed
  158.         local -a -i INDEX=( 0 0 )       # Local of index to return
  159.         local -i Lidx Lcnt
  160.         local -i if=0 of=0              # Default to variable names
  161.         case "$#" in                    # Simplistic option testing
  162.                 0) return 1 ;;
  163.                 1) return 1 ;;
  164.                 2) : ;;                 # Poor man's continue
  165.                 3) case "$1" in
  166.                         -if) if=1 ;;
  167.                         -of) of=1 ;;
  168.                          * ) return 1 ;;
  169.                    esac ; shift ;;
  170.                 4) if=1 ; of=1 ; shift ; shift ;;
  171.                 *) return 1
  172.         esac
  173.         # Make local copy of list
  174.         case "$if" in
  175.                 0) eval LIST=\( \"\$\{$1\[@\]\}\" \) ;;
  176.                 1) LIST=( $(cat $1) ) ;;
  177.         esac
  178.         # Grok (grope?) the array
  179.         Lcnt=${#LIST[@]}
  180.         Lidx=0
  181.         until (( Lidx >= Lcnt ))
  182.         do
  183.         if IsNumber ${LIST[$Lidx]}
  184.         then
  185.                 local -i inode name
  186.                 local ft
  187.                 inode=Lidx
  188.                 local m=${LIST[$Lidx+2]}        # Hard Links field
  189.                 ft=${LIST[$Lidx+1]:0:1}         # Fast-Stat
  190.                 case $ft in
  191.                 b)      ((Lidx+=12)) ;;         # Block device
  192.                 c)      ((Lidx+=12)) ;;         # Character device
  193.                 *)      ((Lidx+=11)) ;;         # Anything else
  194.                 esac
  195.                 name=Lidx
  196.                 case $ft in
  197.                 -)      ((Lidx+=1)) ;;          # The easy one
  198.                 b)      ((Lidx+=1)) ;;          # Block device
  199.                 c)      ((Lidx+=1)) ;;          # Character device
  200.                 d)      ((Lidx+=1)) ;;          # The other easy one
  201.                 l)      ((Lidx+=3)) ;;          # At LEAST two more fields
  202. #  A little more elegance here would handle pipes,
  203. #+ sockets, deleted files - later.
  204.                 *)      until IsNumber ${LIST[$Lidx]} || ((Lidx >= Lcnt))
  205.                         do
  206.                                 ((Lidx+=1))
  207.                         done
  208.                         ;;                      # Not required
  209.                 esac
  210.                 INDEX[${#INDEX[*]}]=$inode
  211.                 INDEX[${#INDEX[*]}]=$name
  212.                 INDEX[0]=${INDEX[0]}+1          # One more "line" found
  213. # echo "Line: ${INDEX[0]} Type: $ft Links: $m Inode: \
  214. # ${LIST[$inode]} Name: ${LIST[$name]}"
  215.         else
  216.                 ((Lidx+=1))
  217.         fi
  218.         done
  219.         case "$of" in
  220.                 0) eval $2=\( \"\$\{INDEX\[@\]\}\" \) ;;
  221.                 1) echo "${INDEX[@]}" > "$2" ;;
  222.         esac
  223.         return 0                                # What could go wrong?
  224. }
  225. # # # # # Content Identify File # # # # #
  226. #
  227. #       DigestFile Input-Array-Name Digest-Array-Name
  228. # or
  229. #       DigestFile -if Input-FileName Digest-Array-Name
  230. # # # # #
  231. # Here document used as a comment block.
  232. : <<DigestFilesDoc
  233. The key (no pun intended) to a Unified Content File System (UCFS)
  234. is to distinguish the files in the system based on their content.
  235. Distinguishing files by their name is just, so, 20th Century.
  236. The content is distinguished by computing a checksum of that content.
  237. This version uses the md5sum program to generate a 128 bit checksum
  238. representative of the file's contents.
  239. There is a chance that two files having different content might
  240. generate the same checksum using md5sum (or any checksum).  Should
  241. that become a problem, then the use of md5sum can be replace by a
  242. cyrptographic signature.  But until then...
  243. The md5sum program is documented as outputting three fields (and it
  244. does), but when read it appears as two fields (array elements).  This
  245. is caused by the lack of whitespace between the second and third field.
  246. So this function gropes the md5sum output and returns:
  247.         [0]     32 character checksum in hexidecimal (UCFS filename)
  248.         [1]     Single character: ' ' text file, '*' binary file
  249.         [2]     Filesystem (20th Century Style) name
  250.         Note: That name may be the character '-' indicating STDIN read.
  251. DigestFilesDoc
  252. DigestFile()
  253. {
  254.         local if=0              # Default, variable name
  255.         local -a T1 T2
  256.         case "$#" in
  257.         3)      case "$1" in
  258.                 -if)    if=1 ; shift ;;
  259.                  * )    return 1 ;;
  260.                 esac ;;
  261.         2)      : ;;            # Poor man's "continue"
  262.         *)      return 1 ;;
  263.         esac
  264.         case $if in
  265.         0) eval T1=\( \"\$\{$1\[@\]\}\" \)
  266.            T2=( $(echo ${T1[@]} | md5sum -) )
  267.            ;;
  268.         1) T2=( $(md5sum $1) )
  269.            ;;
  270.         esac
  271.         case ${#T2[@]} in
  272.         0) return 1 ;;
  273.         1) return 1 ;;
  274.         2) case ${T2[1]:0:1} in         # SanScrit-2.0.5
  275.            \*) T2[${#T2[@]}]=${T2[1]:1}
  276.                T2[1]=\*
  277.                ;;
  278.             *) T2[${#T2[@]}]=${T2[1]}
  279.                T2[1]=" "
  280.                ;;
  281.            esac
  282.            ;;
  283.         3) : ;; # Assume it worked
  284.         *) return 1 ;;
  285.         esac
  286.         local -i len=${#T2[0]}
  287.         if [ $len -ne 32 ] ; then return 1 ; fi
  288.         eval $2=\( \"\$\{T2\[@\]\}\" \)
  289. }
  290. # # # # # Locate File # # # # #
  291. #
  292. #       LocateFile [-l] FileName Location-Array-Name
  293. # or
  294. #       LocateFile [-l] -of FileName Location-Array-FileName
  295. # # # # #
  296. # A file location is Filesystem-id and inode-number
  297. # Here document used as a comment block.
  298. : <<StatFieldsDoc
  299.         Based on stat, version 2.2
  300.         stat -t and stat -lt fields
  301.         [0]     name
  302.         [1]     Total size
  303.                 File - number of bytes
  304.                 Symbolic link - string length of pathname
  305.         [2]     Number of (512 byte) blocks allocated
  306.         [3]     File type and Access rights (hex)
  307.         [4]     User ID of owner
  308.         [5]     Group ID of owner
  309.         [6]     Device number
  310.         [7]     Inode number
  311.         [8]     Number of hard links
  312.         [9]     Device type (if inode device) Major
  313.         [10]    Device type (if inode device) Minor
  314.         [11]    Time of last access
  315.                 May be disabled in 'mount' with noatime
  316.                 atime of files changed by exec, read, pipe, utime, mknod (mmap?)
  317.                 atime of directories changed by addition/deletion of files
  318.         [12]    Time of last modification
  319.                 mtime of files changed by write, truncate, utime, mknod
  320.                 mtime of directories changed by addtition/deletion of files
  321.         [13]    Time of last change
  322.                 ctime reflects time of changed inode information (owner, group
  323.                 permissions, link count
  324. -*-*- Per:
  325.         Return code: 0
  326.         Size of array: 14
  327.         Contents of array
  328.         Element 0: /home/mszick
  329.         Element 1: 4096
  330.         Element 2: 8
  331.         Element 3: 41e8
  332.         Element 4: 500
  333.         Element 5: 500
  334.         Element 6: 303
  335.         Element 7: 32385
  336.         Element 8: 22
  337.         Element 9: 0
  338.         Element 10: 0
  339.         Element 11: 1051221030
  340.         Element 12: 1051214068
  341.         Element 13: 1051214068
  342.         For a link in the form of linkname -> realname
  343.         stat -t  linkname returns the linkname (link) information
  344.         stat -lt linkname returns the realname information
  345.         stat -tf and stat -ltf fields
  346.         [0]     name
  347.         [1]     ID-0?           # Maybe someday, but Linux stat structure
  348.         [2]     ID-0?           # does not have either LABEL nor UUID
  349.                                 # fields, currently information must come
  350.                                 # from file-system specific utilities
  351.         These will be munged into:
  352.         [1]     UUID if possible
  353.         [2]     Volume Label if possible
  354.         Note: 'mount -l' does return the label and could return the UUID
  355.         [3]     Maximum length of filenames
  356.         [4]     Filesystem type
  357.         [5]     Total blocks in the filesystem
  358.         [6]     Free blocks
  359.         [7]     Free blocks for non-root user(s)
  360.         [8]     Block size of the filesystem
  361.         [9]     Total inodes
  362.         [10]    Free inodes
  363. -*-*- Per:
  364.         Return code: 0
  365.         Size of array: 11
  366.         Contents of array
  367.         Element 0: /home/mszick
  368.         Element 1: 0
  369.         Element 2: 0
  370.         Element 3: 255
  371.         Element 4: ef53
  372.         Element 5: 2581445
  373.         Element 6: 2277180
  374.         Element 7: 2146050
  375.         Element 8: 4096
  376.         Element 9: 1311552
  377.         Element 10: 1276425
  378. StatFieldsDoc
  379. #       LocateFile [-l] FileName Location-Array-Name
  380. #       LocateFile [-l] -of FileName Location-Array-FileName
  381. LocateFile()
  382. {
  383.         local -a LOC LOC1 LOC2
  384.         local lk="" of=0
  385.         case "$#" in
  386.         0) return 1 ;;
  387.         1) return 1 ;;
  388.         2) : ;;
  389.         *) while (( "$#" > 2 ))
  390.            do
  391.               case "$1" in
  392.                -l) lk=-1 ;;
  393.               -of) of=1 ;;
  394.                 *) return 1 ;;
  395.               esac
  396.            shift
  397.            done ;;
  398.         esac
  399. # More Sanscrit-2.0.5
  400.       # LOC1=( $(stat -t $lk $1) )
  401.       # LOC2=( $(stat -tf $lk $1) )
  402.       # Uncomment above two lines if system has "stat" command installed.
  403.         LOC=( ${LOC1[@]:0:1} ${LOC1[@]:3:11}
  404.               ${LOC2[@]:1:2} ${LOC2[@]:4:1} )
  405.         case "$of" in
  406.                 0) eval $2=\( \"\$\{LOC\[@\]\}\" \) ;;
  407.                 1) echo "${LOC[@]}" > "$2" ;;
  408.         esac
  409.         return 0
  410. # Which yields (if you are lucky, and have "stat" installed)
  411. # -*-*- Location Discriptor -*-*-
  412. #       Return code: 0
  413. #       Size of array: 15
  414. #       Contents of array
  415. #       Element 0: /home/mszick         20th Century name
  416. #       Element 1: 41e8                 Type and Permissions
  417. #       Element 2: 500                  User
  418. #       Element 3: 500                  Group
  419. #       Element 4: 303                  Device
  420. #       Element 5: 32385                inode
  421. #       Element 6: 22                   Link count
  422. #       Element 7: 0                    Device Major
  423. #       Element 8: 0                    Device Minor
  424. #       Element 9: 1051224608           Last Access
  425. #       Element 10: 1051214068          Last Modify
  426. #       Element 11: 1051214068          Last Status
  427. #       Element 12: 0                   UUID (to be)
  428. #       Element 13: 0                   Volume Label (to be)
  429. #       Element 14: ef53                Filesystem type
  430. }
  431. # And then there was some test code
  432. ListArray() # ListArray Name
  433. {
  434.         local -a Ta
  435.         eval Ta=\( \"\$\{$1\[@\]\}\" \)
  436.         echo
  437.         echo "-*-*- List of Array -*-*-"
  438.         echo "Size of array $1: ${#Ta[*]}"
  439.         echo "Contents of array $1:"
  440.         for (( i=0 ; i<${#Ta[*]} ; i++ ))
  441.         do
  442.             echo -e "\tElement $i: ${Ta[$i]}"
  443.         done
  444.         return 0
  445. }
  446. declare -a CUR_DIR
  447. # For small arrays
  448. ListDirectory "${PWD}" CUR_DIR
  449. ListArray CUR_DIR
  450. declare -a DIR_DIG
  451. DigestFile CUR_DIR DIR_DIG
  452. echo "The new \"name\" (checksum) for ${CUR_DIR[9]} is ${DIR_DIG[0]}"
  453. declare -a DIR_ENT
  454. # BIG_DIR # For really big arrays - use a temporary file in ramdisk
  455. # BIG-DIR # ListDirectory -of "${CUR_DIR[11]}/*" "/tmpfs/junk2"
  456. ListDirectory "${CUR_DIR[11]}/*" DIR_ENT
  457. declare -a DIR_IDX
  458. # BIG-DIR # IndexList -if "/tmpfs/junk2" DIR_IDX
  459. IndexList DIR_ENT DIR_IDX
  460. declare -a IDX_DIG
  461. # BIG-DIR # DIR_ENT=( $(cat /tmpfs/junk2) )
  462. # BIG-DIR # DigestFile -if /tmpfs/junk2 IDX_DIG
  463. DigestFile DIR_ENT IDX_DIG
  464. # Small (should) be able to parallize IndexList & DigestFile
  465. # Large (should) be able to parallize IndexList & DigestFile & the assignment
  466. echo "The \"name\" (checksum) for the contents of ${PWD} is ${IDX_DIG[0]}"
  467. declare -a FILE_LOC
  468. LocateFile ${PWD} FILE_LOC
  469. ListArray FILE_LOC
  470. exit 0