Advertisement
devinteske

readvars() -- a multiline "read" implementation for /bin/sh

Aug 17th, 2015
567
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 6.19 KB | None | 0 0
  1. #!/bin/sh
  2.  
  3. # readvars [-d] [-f filter] [-F field:filter] [-L limit] [-n "var ..."] \
  4. #          [-s sep [-c]] [-t var] [--] cmd ...
  5. #
  6. # For each line of output generated by ``cmd ...'', set a series of numbered
  7. # variables (default "line1", "line2", etc.). Also sets a variable containing
  8. # the number of lines processed (default "lines"). If ``cmd ...'' is "-", NULL,
  9. # or missing, lines are read instead from stdin.
  10. #
  11. # If `-d' is given, debugging information is provided on stderr.
  12. #
  13. # If ``-f filter'' is given, only lines matching filter regular expression are
  14. # processed.
  15. #
  16. # If ``-F field:filter'' is given, only lines where numbered field matches the
  17. # filter regular expression are processed. If field is zero, acts the same as
  18. # ``-f filter''.
  19. #
  20. # If ``-L limit'' is given, process at-most limit number of lines. When
  21. # combined with ``-f filter'', process at-most limit number of lines matching
  22. # filter regular expression. Default value is `0' and to process all lines.
  23. #
  24. # If ``-n "foo bar baz rest"'' is given, the first word on the first line is
  25. # assigned to variable "foo1", 2nd-word 1st-line to "bar1", 3rd-word 1st-line
  26. # to "baz1", and any words remaining on line-1 are assigned to "rest1". The
  27. # last variable name always contains the remainder of the line. Default value
  28. # if not present is simply "line".
  29. #
  30. # If ``-s sep'' is given, use sep as the separator between fields. If `-c' is
  31. # given, the space between sequential separators are counted as empty words.
  32. #
  33. # If ``-t variable'' is given, the total number of lines processed is assigned
  34. # to "variable" (default value is "lines")..
  35. #
  36. # If `--' is given, any further potential option flags are taken as part of
  37. # command to execute.
  38. #
  39. #
  40. # The return status is that of ``cmd [args ...]''.
  41. #
  42. readvars()
  43. {
  44.     local OPTIND=1 OPTFLAG __readvars_flag
  45.     local __readvars_count_nulls=
  46.     local __readvars_debug=
  47.     local __readvars_filter=
  48.     local __readvars_Ffilter=
  49.     local __readvars_Ffilter_field=
  50.     local __readvars_limit=0
  51.     local __readvars_sep=
  52.     local __readvars_total=
  53.     local __readvars_vars=
  54.  
  55.     while getopts cdf:F:L:n:s:t: __readvars_flag; do
  56.         case "$__readvars_flag" in
  57.         c) __readvars_count_nulls=1 ;;
  58.         d) __readvars_debug=1 ;;
  59.         f) __readvars_filter="$OPTARG" ;;
  60.         F) __readvars_Ffilter="${OPTARG#*:}"
  61.            __readvars_Ffilter_field="${OPTARG%%:*}" ;;
  62.         L) __readvars_limit="$OPTARG" ;;
  63.         n) __readvars_vars="$OPTARG" ;;
  64.         s) __readvars_sep="$OPTARG" ;;
  65.         t) __readvars_total="$OPTARG" ;;
  66.         esac
  67.     done
  68.     shift $(( $OPTIND - 1 ))
  69.  
  70.     eval "$( awk \
  71.             -F "${__readvars_sep:-$IFS}" \
  72.             -v IFS="$IFS" \
  73.             -v count_nulls="${__readvars_count_nulls:-0}" \
  74.             -v debug="${__readvars_debug:-0}" \
  75.             -v filter="$__readvars_filter" \
  76.             -v Ffilter="$__readvars_Ffilter" \
  77.             -v Ffilter_field="${__readvars_Ffilter_field:-0}" \
  78.             -v sep="${__readvars_sep:-$IFS}" \
  79.             -v total="${__readvars_total:-lines}" \
  80.             -v vars="${__readvars_vars:-line}" \
  81.         -- '
  82.         ############################################ FUNCTIONS
  83.  
  84.         function esc(str) { gsub(/'\''/, "&\\\\&&", str); return str }
  85.  
  86.         function set(k, v)
  87.         {
  88.             parent_eval = sprintf("%s='\''%s'\''", k, esc(v))
  89.             if (debug) print "DEBUG:", parent_eval > "/dev/stderr"
  90.             print parent_eval
  91.         }
  92.  
  93.         function seti(key, val) { set(key i, val) }
  94.  
  95.         function extract_value(line, varnum)
  96.         {
  97.             match(line, sprintf(count_nulls ? "^[^%s]*[%s]?" : \
  98.                 "^[%s]*[^%s]+", sep, sep))
  99.             if (varnum == nvars) {
  100.                 value = substr(line, RSTART)
  101.                 sub(sprintf("[%s]*$", sep), "", value)
  102.             } else
  103.                 value = substr(line, RSTART, RLENGTH)
  104.             sub(sprintf(count_nulls ? \
  105.                 "[%s]?$" : "^[%s]*", sep), "", value)
  106.             seti(varlist[varnum], value)
  107.             return substr(line, RSTART + RLENGTH)
  108.         }
  109.  
  110.         function setivars(vars, line)
  111.         {
  112.             for (v = 1; v <= nvars; v++)
  113.                 line = extract_value(line, v)
  114.         }
  115.  
  116.         function match_filters(line)
  117.         {
  118.             return (line ~ filter && $Ffilter_field ~ Ffilter)
  119.         }
  120.  
  121.         ############################################ MAIN
  122.  
  123.         BEGIN {
  124.             nvars = split(vars, varlist, "[" IFS "]+")
  125.  
  126.             cmd = "eval"
  127.             if (ARGC <= 1 || (ARGC == 2 && ARGV[1] == "-")) {
  128.                 ARGC = 2
  129.                 ARGV[1] = "cat"
  130.             }
  131.             for (a = 1; a < ARGC; a++)
  132.                 cmd = cmd " '\''" esc(ARGV[a]) "'\''"
  133.  
  134.             retval = 0
  135.             while (cmd | getline)
  136.             {
  137.                 if (match_filters($0)) {
  138.                     if (++i > (limit > 0 ? limit : i)) {
  139.                         retval = close(cmd)
  140.                         exit
  141.                     }
  142.                     setivars(varlist, $0)
  143.                 }
  144.             }
  145.             retval = close(cmd)
  146.             exit
  147.         }
  148.  
  149.         END {
  150.             set(total, i == "" ? 0 : i)
  151.             printf "( exit %u )", int(retval / 256)
  152.         }
  153.     ' "$@" )"
  154. }
  155.  
  156. ### BEGIN UNIT-TEST CODE
  157. #
  158. #readvars "$@"
  159. #echo "?=[$?]"
  160. #
  161. #args=line total=lines
  162. #while getopts cdf:L:n:s:t: flag; do
  163. #   case "$flag" in
  164. #   n) args="$OPTARG" ;;
  165. #   t) total="$OPTARG" ;;
  166. #   esac
  167. #done
  168. #shift $(( $OPTIND - 1 ))
  169. #
  170. #eval count=\"\$$total\"
  171. #echo "$total=[$count]"
  172. #n=1
  173. #while [ $n -le $count ]; do
  174. #   for arg in $args; do
  175. #       eval value=\"\$$arg$n\"
  176. #       echo "$arg$n=[$value]"
  177. #   done
  178. #   n=$(( $n + 1 ))
  179. #done
  180. #
  181. ### END UNIT-TEST CODE
  182.  
  183. #
  184. # Examples:
  185. #
  186.  
  187. echo ">>> Example 1. List of users in passwd(5)"
  188. readvars -cs: -n"user rest" < /etc/passwd
  189. echo "?=[$?]"
  190. n=1
  191. while [ $n -le ${lines:-0} ]; do
  192.     eval user=\"\$user$n\"
  193.     echo "$user"
  194.     n=$(( $n + 1 ))
  195. done
  196. echo
  197.  
  198. echo ">>> Example 2. Get homedir of a specific user in passwd(5)"
  199. PASSWD_FIELDS="user hash uid gid gecos home shell"
  200. unset home1
  201. readvars -cs: -n"$PASSWD_FIELDS" -F1:'^root$' -L1 < /etc/passwd
  202. echo "?=[$?]"
  203. echo "root's home directory is: ${home1:-**UNKNOWN**}"
  204. echo
  205.  
  206. echo ">>> Example 3. List primary group membership names for each user"
  207. PASSWD_FIELDS="user hash uid gid gecos home shell"
  208. GROUP_FIELDS="group chkgrp_hash group_gid members"
  209. readvars -t nusers -cs: -n"$PASSWD_FIELDS" < /etc/passwd
  210. echo "?=[$?]"
  211. readvars -t ngroups -cs: -n"$GROUP_FIELDS" < /etc/group
  212. echo "?=[$?]"
  213. g=1
  214. while [ $g -le ${ngroups:-0} ]; do
  215.     eval group=\"\$group$g\" group_gid=\"\$group_gid$g\"
  216.     eval group_name_$group_gid=\"\$group$g\"
  217.     g=$(( $g + 1 ))
  218. done
  219. u=1
  220. while [ $u -le ${nusers:-0} ]; do
  221.     eval user=\"\$user$u\" user_gid=\"\$user_gid$u\"
  222.     eval user_group=\"\$group_name_$user_gid\"
  223.     echo "user=[$user] user_group=[$user_group]"
  224.     u=$(( $u + 1 ))
  225. done
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement