shell dot on steroids https://pagure.io/shellfu

ini.sh 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/bin/bash
  2. ffoo import core
  3. #
  4. # Expected filename extension (for guessing from -p path head)
  5. #
  6. # If no filename to read is given, iniread will guess filename
  7. # as the path head plus this suffix (e.g. `foo.ini` for
  8. # `iniread -p foo.bar.baz`)
  9. #
  10. FFOO_INI_SUFFIX=".ini"
  11. __iniread__cat() {
  12. # cat without starting a process
  13. while IFS= read line;
  14. do echo "$line"; done
  15. }
  16. __initead_fltcmt() {
  17. grep -v -e "^[[:space:]]*[#;]" -e "^[[:space:]]*$"
  18. }
  19. __iniread__fltkey() {
  20. # filter values from key=value pair
  21. local line
  22. local key
  23. local value
  24. while IFS= read line;
  25. do
  26. line=$(sed -re 's/^\s*//' <<<"$line")
  27. key=$(sed -re 's/\s*=.*//; s/^\s*//' <<<"$line")
  28. if $strict;
  29. then
  30. value=$(sed -re 's/[^=]*=//' <<<"$line")
  31. else
  32. value=$(sed -re 's/[^=]*=\s*//' <<<"$line")
  33. fi
  34. if test "$key" = "$wntkey";
  35. then
  36. echo "$value"
  37. fi
  38. done
  39. }
  40. __iniread__fltsct() {
  41. # filter per section
  42. local sct_ok=false
  43. local line
  44. while IFS= read line;
  45. do
  46. if grep -qse "^\[$wntsct\]" <<<"$line";
  47. then
  48. sct_ok=true
  49. elif grep -qse "^\[" <<<"$line";
  50. then
  51. sct_ok=false
  52. elif $sct_ok;
  53. then
  54. $strict || line="$(sed -re 's/^\s*//' <<<"$line")"
  55. echo "$line"
  56. fi
  57. done
  58. }
  59. __iniread__merge() {
  60. if test -z "$1";
  61. then # guess filename from section head
  62. local guess="$(cut -d. -f1 <<<"$wntsct")$FFOO_INI_SUFFIX"
  63. debug -v guess
  64. __iniread__merge "$guess"
  65. fi
  66. local arg trydir trypath
  67. for arg in "$@";
  68. do
  69. case $arg in
  70. -|*/*) # stdin, or path (with slash)
  71. cat $arg
  72. ;;
  73. *) # name given, find all its incarnations
  74. debug -v FFOO_INI_PATH
  75. echo "$FFOO_INI_PATH" \
  76. | tr ':' '\n' \
  77. | while read trydir;
  78. do
  79. test -z "$trydir" && continue
  80. trypath="$trydir/$arg"
  81. debug -v trypath
  82. cat $trypath 2>/dev/null
  83. done
  84. ;;
  85. esac
  86. done
  87. true
  88. }
  89. iniread() {
  90. #
  91. # A flexible (not just) .ini file reader
  92. #
  93. # Usage:
  94. # iniread [-S] [-1] [-s section] [-k key] [file...]
  95. # iniread [-S] [-1] [-p path] [file...]
  96. #
  97. # With *file* as only argument, iniread only throws away
  98. # comments (i. e. lines starting with "#") and empty lines.
  99. #
  100. # Probably most apparent use case is selection of key from
  101. # a section of INI file. This can be achieved using
  102. # *path*, which is equivalent to providing both *key* and
  103. # *section* (i.e. `-p section.key` is the same as
  104. # `-s section -k key`).
  105. #
  106. # However, you can also provide only *section* to select
  107. # only that section (leaving any key=value pairs intact),
  108. # or provide only *key* to select only that key from *all*
  109. # sections in file (if there are any). This in fact extends
  110. # use of this function beyond just INI-like format: if you
  111. # are brave enough, you could e.g. query a shell script
  112. # for a variable assignment (i.e. key=value pair).
  113. #
  114. # If suitable key is found at multiple lines, all values
  115. # are printed, unless you provided *-1* option. This allows
  116. # for creating multi-line values.
  117. #
  118. # Leading/trailing spaces and empty lines are removed from
  119. # output by default, but strict mode (*-S* or *--strict*
  120. # switch) changes this behavior so that in theory you can
  121. # have e.g. a PEP8-compliant Python script inside an INI
  122. # file.
  123. #
  124. # If *file* argument contains slash, it is expanded as a
  125. # regular path. Alternatively, `-` (single dash) can be
  126. # specified, which results in STDIN being read instead.
  127. #
  128. # If *file* argument does not contain slash, it is searched
  129. # in directories given in `$FFOO_INI_PATH`, and any files
  130. # that are found are simply concatenated. (This means
  131. # that if section is queried that is present in both files,
  132. # it is effectively concatenated as well.
  133. #
  134. # In case of multiple *file* arguments, each is processed
  135. # separately as indicated above, and result is concatenated
  136. # before filtering.
  137. #
  138. # Without *file* given at all, same procedure is used as
  139. # in case of filename without slash, except that *file*
  140. # argument is inferred by taking part of section name
  141. # before first dot and adding `$FFOO_INI_SUFFIX`, which
  142. # is ".ini" by default. This allows for creating relatively
  143. # rich config value structure, having it divided in several
  144. # files (e.g. by component) and from within your scripts,
  145. # still accessing them with as little typing as possible.
  146. #
  147. local wntsct=""
  148. local wntkey=""
  149. local strict=false
  150. local sct_ok=true
  151. local one_line=false
  152. local grepex="." # i.e. throw away empty lines
  153. while true; do case $1 in
  154. --) break ;;
  155. -1|--one-line) one_line=true; shift 1 ;;
  156. -k|--key) wntkey="$2"; shift 2 ;;
  157. -p|--path) wntkey="${2##*.}"; wntsct="${2%.$wntkey}"; shift 2 ;;
  158. -s|--section) wntsct="$2"; sct_ok=false; shift 2 ;;
  159. -S|--strict) strict=true; shift 1 ;;
  160. -l|-L) warn "--list-* not implemented yet"; shift 1 ;;
  161. *) break ;;
  162. esac done
  163. fltsct=__iniread__cat
  164. fltkey=__iniread__cat
  165. fltcmt=__initead_fltcmt
  166. limit_line=__iniread__cat
  167. test -n "$wntsct" && fltsct=__iniread__fltsct
  168. test -n "$wntkey" && fltkey=__iniread__fltkey
  169. $one_line && limit_line="tail -1"
  170. $strict && grepex=""
  171. $strict && fltcmt=__iniread__cat
  172. debug -v wntkey wntsct
  173. debug "\$@='$@'"
  174. __iniread__merge "$@" \
  175. | $fltcmt \
  176. | $fltsct \
  177. | $fltkey \
  178. | $limit_line \
  179. | grep "$grepex"
  180. #TODO: list keys as section/key (with -s, list keys only)
  181. }