Пример A-27. Повторение основ

Пример A-27. Повторение основ

  1. #!/bin/bash
  2. # basics-reviewed.bash
  3. # Расширение файла == *.bash == сценарий, использующий особенности Bash
  4. #   Copyright (c) Michael S. Zick, 2003; All rights reserved.
  5. #   License: Use in any form, for any purpose.
  6. #   Издание: $ID$
  7. #
  8. #              Правка, с целью улучшения оформления, выполнена автором книги.
  9. #   ("Advanced Bash Scripting Guide")
  10. #  Этот сценарий тестировался в Bash версий 2.04, 2.05a и 2.05b.
  11. #  Он может не работать в более ранних версиях.
  12. #  Этот сценарий умышленно генерирует ошибку
  13. #+ "command not found". См. строку 394.
  14. #  Ведущий разработчик Bash, Chet Ramey, обязался исправить эту проблему
  15. #+ в следующих версиях Bash.
  16.         ###-------------------------------------------###
  17.         ###  Сценарий выводит много информации на     ###
  18.         ###+ экран, поэтому запускайте его в конвейере###
  19.         ###+ с командой more                          ###
  20.         ###                                           ###
  21.         ###  Кроме того, вы можете перенаправить      ###
  22.         ###+ вывод в файл, с целью последующего       ###  
  23.         ###+ изучения                                 ###  
  24.         ###-------------------------------------------###
  25. #  Большая часть из приводимых здесь моментов описывается
  26. #+ в вышеупомянутой книге "Advanced Bash Scripting Guide."
  27. #  Этот сценарий, по сути можно расценивать как своего рода презентацию.
  28. #      — msz
  29. # Переменные не типизированы, если не указано обратное.
  30. #  Соглашения по именованию переменных.
  31. #  Имена переменных не должны начинаться с цифровых символов.
  32. #  Имена дескрипторов файлов (как например: 2>&1)
  33. #+ должны содержать ТОЛЬКО цифры.
  34. # Параметры и элементы массивов Bash — пронумерованы.
  35. # (Параметры, в этом смысле, очень похожи на массивы.)
  36. # Переменные в Bash могут иметь неопределенное значение.
  37. unset VarNull
  38. # Переменные в Bash могут быть опеределены, но содержать "пустое" (null) значение.
  39. VarEmpty=''
  40. # Переменные могут быть определены и содержать некоторое, непустое значение
  41. VarSomething='Literal'
  42. # Переменные могут хранить:
  43. #   * Целое 32-битовое число со знаком
  44. #   * Строку символов
  45. # Переменные могут быть массивом.
  46. #  Строки могут содержать пробелы и интерпретироваться
  47. #+ как вызов функции с аргументами.
  48. #  Имена переменных и имена функций
  49. #+ находятся в разных пространствах имен (namespaces).
  50. #  Переменные могут быть объявлены массивами как явно, так и неявно
  51. #+ в зависимости от семантики операции присваивания.
  52. #  Явно:
  53. declare -a ArrayVar
  54. # Команда echo — внутренняя команда.
  55. echo $VarSomething
  56. # Команда printf — внутренняя команда.
  57. # здесь %s интерпретируется как строка формата
  58. printf %s $VarSomething         # Перевод строки отсутствует, ничего не выводится.
  59. echo                            # Выводит только перевод строки.
  60. # Интерпретатор Bash различает отдельные слова по символу пробела между ними.
  61. # Поэтому наличие или отсутствие пробела — очень важный признак.
  62. # (В общем случае это так, но есть и исключения из правил.)
  63. # Символ "доллара" ($) интерпретируется как: Content-Of (содержимое для ...).
  64. # Расширенный синтаксис, с использованием символа "доллара":
  65. echo ${VarSomething}
  66. #  Здесь, конструкция  ${ ... }, позволяет указывать
  67. #+ не только имена переменных.
  68. #  Как правило, запись $VarSomething
  69. #+ всегда может быть представлена в виде : ${VarSomething}.
  70. # Чтобы увидеть следующие операции в действии — вызовите сценарий
  71. #+ с несколькими входными аргументами.
  72. #  За пределами двойных кавычек, специальные символы @ и *
  73. #+ имеют идентичное назначение.
  74. #  Может произноситься как: All-Elements-Of (Все-Элементы-Для).
  75. #  Если имя переменной не указано, то эти специальные символы
  76. #+ применяются к предопределенным переменным Bash.
  77. echo $*                         # Все входные параметры сценария или функции
  78. echo ${*}                       # То же самое
  79. # Bash запрещает подстановку имен файлов в вышеупомянутых конструкциях.
  80. # Ссылка на Все-Элементы-Для
  81. echo $@                         # то же самое, что и выше
  82. echo ${@}                       # то же самое
  83. #  Внутри двойных кавычек, поведение символов @ и *
  84. #+ зависит от установки переменной IFS (Input Field Separator — Разделитель Полей).
  85. #  Ссылка на Все-Элементы-Для, внутри двойных кавычек, работает точно так же.
  86. # Обращение к имени переменной означает получение
  87. #+ всех элементов (символов) строки.
  88. #  Для обращения к отдельным элементам (символам) строки,
  89. #+ может использоваться расширенный синтаксис (см. ниже).
  90. #  Обращение к имени переменной-массива в Bash
  91. #+ означает обращение к нулевому элементу массива,
  92. #+ а НЕ к ПЕРВОМУ ОПРЕДЕЛЕННОМУ или к ПЕРВОМУ ИНИЦИАЛИЗИРОВАННОМУ элементу.
  93. #  Для обращения к другим элементам массива, необходимо явное указание элемента,
  94. #+ это означает, что ДОЛЖЕН использоваться расширенный синтаксис.
  95. #  В общем случае: ${name[subscript]}.
  96. #  Для строк может использоваться такая форма записи: ${name:subscript},
  97. #+ а также для обращения к нулевому элементу массива.
  98. # Массивы в Bash реализованы как связанные списки,
  99. #+ а не как фиксированная область памяти, что характерно для некоторых
  100. #+ языков программирования.
  101. #   Характеристики массивов в Bash:
  102. #   -------------------------------
  103. #   Если не определено иначе, индексация массивов в Bash
  104. #+  начинается с нуля: [0]
  105. #   Это называется "индексация с нуля".
  106. ###
  107. #   Если не указано иначе, массивы в Bash являются упакованными
  108. #+  (т.е. массивы просто не содержат элементов с отсутствующими индексами).
  109. ###
  110. #   Отрицательные индексы недопустимы.
  111. ###
  112. #   Элементы массива не обязательно должны быть одного и того же типа.
  113. ###
  114. #   Элементы массива могут быть неинициализированы.
  115. #       Т.е. массив может быть "разреженным"
  116. ###
  117. #   Элементы массива могут быть инициализированы пустым значением.
  118. ###
  119. #   Элементы массива могут содержать:
  120. #     * Целое 32-битовое число со знаком
  121. #     * Строку
  122. #     * Форматированную строку, которая выглядит
  123. #     + как вызов к функции с параметрами
  124. ###
  125. #   Инициализированные элементы массива могут быть  деинициализированы (unset).
  126. #       Т.е. массив может быть переупакован так,
  127. #   +   что он не будет содержать элемента с данным индексом.
  128. ###
  129. #   К массиву могут добавляться дополнительные элементы,
  130. #+  не определенные ранее.
  131. ###
  132. # По этим причинам я называю массивы Bash — "Bash-массивами" ("Bash-Arrays").
  133. #
  134. #     — msz
  135. #  Демонстрация вышесказанного — инициализируем ранее объявленный массив ArrayVar
  136. #+ как "разреженный" массив.
  137. #  (команда 'unset ... ' используется для демонстрации вышесказанного.)
  138. unset ArrayVar[0]                   # Для демонстрации
  139. ArrayVar[1]=one                     # Без кавычек
  140. ArrayVar[2]=''                      # Инициализация пустым значением
  141. unset ArrayVar[3]                   # Для демонстрации
  142. ArrayVar[4]='four'                  # В кавычках
  143. # Строка формата %q — трактуется как: Quoted-Respecting-IFS-Rules
  144. #+ (в соответствии с установками IFS).
  145. echo
  146. echo '- - Вне двойных кавычек - -'
  147. ###
  148. printf %q ${ArrayVar[*]}            # Шаблон "Все-Элементы-Для"
  149. echo
  150. echo 'команда echo:'${ArrayVar[*]}
  151. ###
  152. printf %q ${ArrayVar[@]}            # "Все-Элементы-Для"
  153. echo
  154. echo 'команда echo:'${ArrayVar[@]}
  155. # Двойные кавычки используются для разрешения операции подстановки
  156. #+ внутри кавычек.
  157. # Существует пять самых распространенных случаев,
  158. #+ зависящих от установок  переменной IFS.
  159. echo
  160. echo '- - В двойных кавычках - По-умолчанию IFS содержит пробел-табуляцию-перевод строки- -'
  161. IFS=$'\x20'$'\x09'$'\x0A'           #  Три байта,
  162.                                     #+ и именно в таком порядке.
  163. printf %q "${ArrayVar[*]}"          # Шаблон "Все-Элементы-Для"
  164. echo
  165. echo 'команда echo:'"${ArrayVar[*]}"
  166. ###
  167. printf %q "${ArrayVar[@]}"          # "Все-Элементы-Для"
  168. echo
  169. echo 'команда echo:'"${ArrayVar[@]}"
  170. echo
  171. echo '- - В двойных кавычках - Первый символ в IFS: ^ - -'
  172. # Любой печатаемый, непробельный символ, дает тот же эффект.
  173. IFS='^'$IFS                         # ^ + пробел табуляция перевод строки
  174. ###
  175. printf %q "${ArrayVar[*]}"          # Шаблон "Все-Элементы-Для"
  176. echo
  177. echo 'команда echo:'"${ArrayVar[*]}"
  178. ###
  179. printf %q "${ArrayVar[@]}"          # "Все-Элементы-Для"
  180. echo
  181. echo 'команда echo:'"${ArrayVar[@]}"
  182. echo
  183. echo '- - В двойных кавычках - IFS не содержит пробела - -'
  184. IFS='^:%!'
  185. ###
  186. printf %q "${ArrayVar[*]}"          # Шаблон "Все-Элементы-Для"
  187. echo
  188. echo 'команда echo:'"${ArrayVar[*]}"
  189. ###
  190. printf %q "${ArrayVar[@]}"          # "Все-Элементы-Для"
  191. echo
  192. echo 'команда echo:'"${ArrayVar[@]}"
  193. echo
  194. echo '- - В двойных кавычках - переменная IFS пуста - -'
  195. IFS=''
  196. ###
  197. printf %q "${ArrayVar[*]}"          # Шаблон "Все-Элементы-Для"
  198. echo
  199. echo 'команда echo:'"${ArrayVar[*]}"
  200. ###
  201. printf %q "${ArrayVar[@]}"          # "Все-Элементы-Для"
  202. echo
  203. echo 'команда echo:'"${ArrayVar[@]}"
  204. echo
  205. echo '- - В двойных кавычках - переменная IFS не определена - -'
  206. unset IFS
  207. ###
  208. printf %q "${ArrayVar[*]}"          # Шаблон "Все-Элементы-Для" All-Elements-Of
  209. echo
  210. echo 'команда echo:'"${ArrayVar[*]}"
  211. ###
  212. printf %q "${ArrayVar[@]}"          # "Все-Элементы-Для"
  213. echo
  214. echo 'команда echo:'"${ArrayVar[@]}"
  215. # Вернем переменную IFS в первоначальное состояние,
  216. # записав в нее значение по-умолчанию.
  217. IFS=$'\x20'$'\x09'$'\x0A'           # точно в таком порядке.
  218. # Интерпретация результатов, полученных выше:
  219. #   Форма ввыода по шаблону "Все-Элементы-Для" зависит от содержимого переменной IFS.
  220. ###
  221. #   Простой вывод "Всех-Элементов-Для" не зависит от содержимого переменной IFS.
  222. ###
  223. #   Обратите внимание на различия, имеющиеся в выводе
  224. #+  от команд echo и printf с форматом %q.
  225. #  Давайте вспомним:
  226. #   Параметры очень похожи на массивы и имеют сходное поведение.
  227. ###
  228. #  Примеры выше показывают, что для того, чтобы вывести разреженный
  229. #+  массив полностью, необходимо писать дополнительный код.
  230. ###
  231. # Длина строки равна количеству ненулевых элементов (символов):
  232. echo
  233. echo '- - Имя переменной употребляется вне кавычек - -'
  234. echo 'Количество ненулевых символов: '${#VarSomething}'.'
  235. # test='Lit'$'\x00''eral'           # $'\x00' — нулевой (null) символ.
  236. # echo ${#test}                     # Что получится?
  237. #  Длина массива равна количеству инициализированных элементов,
  238. #+ включая элементы, инициализированные пустыми значениями.
  239. echo
  240. echo 'Количество инициализированных элементов в массиве: '${#ArrayVar[@]}'.'
  241. # Это НЕ максимальный индекс массива (4).
  242. # Это НЕ ширина диапазона (1 . . 4 включительно).
  243. # Это длина связного списка.
  244. ###
  245. #  Максимальный номер индекса массива и диапазон индексов
  246. #+ могут быть найдены, но для этого потребуется дополнительный код.
  247. # Длина строки равна количеству ненулевых элементов (символов):
  248. echo
  249. echo '- - Имя переменной употребляется в кавычках - -'
  250. echo 'Количество непустых символов: '"${#VarSomething}"'.'
  251. #  Длина массива равна количеству инициализированных элементов,
  252. #+ включая элементы, инициализированные пустыми значениями.
  253. echo
  254. echo 'Количество инициализированных элементов в массиве: '"${#ArrayVar[*]}"'.'
  255. #  Вывод: Конструкция ${# ... } не производит подстановку.
  256. #  Совет:
  257. #  Всегда используйте символ All-Elements-Of (Все-Элементы-Для)
  258. #+ если желаете получить результат, не зависящий от содержимого переменной IFS.
  259. #  Определим простую функцию.
  260. #  Я включил в имя функции символ подчеркивания
  261. #+ чтобы как-то обозначить, что это функция, а не переменная.
  262. ###
  263. #  Bash различает имена функций и переменных,
  264. #+ размещая из в различных пространствах имен.
  265. ###
  266. _simple() {
  267.     echo -n 'SimpleFunc'$@          #  Символ перевода строки в любом случае
  268. }                                   #+ будет "съеден".
  269. # Конструкция ( ... ) вызывает команду или функцию.
  270. # Форма записи $( ... ) произносится как: Result-Of (Результат-Выполнения).
  271. # Вызовем функцию _simple
  272. echo
  273. echo '- - Результат работы функции _simple - -'
  274. _simple                             # Попробуйте передать несколько аргументов.
  275. echo
  276. # или
  277. (_simple)                           # Попробуйте передать несколько аргументов.
  278. echo
  279. echo '- Существует ли переменная с таким именем? -'
  280. echo $_simple not defined           # Нет переменной с таким именем.
  281. # Обращение к результату выполнения функции _simple
  282. # (будет получено сообщение об ошибке)
  283. ###
  284. $(_simple)                          # Генерирует сообщение об ошибке:
  285. #                          line 394: SimpleFunc: command not found
  286. #                          ---------------------------------------
  287. echo
  288. ###
  289. #  Причина ошибки вполне очевидна: результат работы функции _simple не есть
  290. #+ ни команда Bash, ни имя определенной ранее функции.
  291. ###
  292. # Этот пример показывает, что вывод от функции _simple подвергается
  293. #+ дополнительной интерпретации.
  294. ###
  295. # Вывод:
  296. #   Функция может использоваться для генерации команд Bash.
  297. # Простая функция, которая выводит команду bash:
  298. ###
  299. _print() {
  300.     echo -n 'printf %q '$@
  301. }
  302. echo '- - Результат работы функции _print - -'
  303. _print parm1 parm2                  # Простой вывод — НЕ команда.
  304. echo
  305. $(_print parm1 parm2)               #  Исполняет команду printf %q parm1 parm2
  306.                                     #  См. пример с IFS выше
  307.                                     #+ на предмет дополнительных возможнойстей.
  308. echo
  309. $(_print $VarSomething)             # Вполне предсказуемый результат.
  310. echo
  311. # Переменные-функции
  312. # ------------------
  313. echo
  314. echo '- - Переменные-функции - -'
  315. # Переменная может хранить целое число, строку или массив.
  316. # Строка может интерпретироваться как вызов функции.
  317. # set -vx                           #  Раскомментарьте при желании
  318. declare -f funcVar                  # в пространстве имен функций!
  319. funcVar=_print                      # Записать имя функции.
  320. $funcVar parm1                      # Аналогично вызову функции _print.
  321. echo
  322. funcVar=$(_print )                  # Результат работы функции.
  323. $funcVar                            # Нет ни ввода, ни вывода.
  324. $funcVar $VarSomething              # Предсказуемый результат.
  325. echo
  326. funcVar=$(_print $VarSomething)     #  Здесь выполняется подстановка
  327.                                     #+ значения переменной $VarSomething.
  328. $funcVar                            #  Содержимое переменной $VarSomething
  329. echo                                #+ стало частью переменной $funcVar
  330. funcVar="$(_print $VarSomething)"   #  Здесь выполняется подстановка
  331.                                     #+ значения переменной $VarSomething.
  332. $funcVar                            #  Содержимое переменной $VarSomething
  333. echo                                #+ стало частью переменной $funcVar
  334. #  Различия в применении или неприменении двойных кавычек
  335. #+ объясняются в примере "protect_literal.sh".
  336. #  В первом случае Bash обрабатывает строку как два отдельных слова,
  337. #  во втором — как одно слово в кавычках с пробелом внутри слова.
  338. # Отложенная подстановка
  339. # ----------------------
  340. echo
  341. echo '- - Отложенная подстановка - -'
  342. funcVar="$(_print '$VarSomething')" # Подстановка значения переменной не производится.
  343. eval $funcVar                       # Подстановка производится ЗДЕСЬ.
  344. echo
  345. VarSomething='NewThing'
  346. eval $funcVar                       # Подстановка производится ЗДЕСЬ.
  347. echo
  348. # Восстановим прежнее значение переменной VarSomething.
  349. VarSomething=Literal
  350. #  В примерах "protect_literal.sh" и "unprotect_literal.sh"
  351. #+ вы найдете две функции, которые выполняют отложенную подстановку
  352. #+ значений переменных.
  353. # ОБЗОР:
  354. # -----
  355. #  Строки могут рассматриваться как классический массив элементов-символов.
  356. #  Строковые операции воздействуют на все элементы (символы) строки
  357. ###
  358. #  Запись: ${array_name[@]} представляет все элементы
  359. #+ Bash-Массива: array_name.
  360. ###
  361. #  Строковые операции, в расширенном синтаксисе, могут манипулировать
  362. #+ всеми элементами массива сразу.
  363. ###
  364. #  Эта способность может рассматриваться как операция For-Each над вектором строк.
  365. ###
  366. #  Параметры подобны массивам.
  367. #  Различия в параметрах для функции и сценария касаются только параметра
  368. #+ ${0}, который никогда не изменяется.
  369. ###
  370. #  Нулевой параметр сценария содержит имя файла сценария
  371. ###
  372. #  Нулевой параметр функции НЕ СОДЕРЖИТ имени функции.
  373. #  Имя функции хранится в служебной переменной $FUNCNAME.
  374. ###
  375. echo
  376. echo '- - Тест (без изменения содержимого переменной) - -'
  377. echo '- неинициализированная переменная -'
  378. echo -n ${VarNull-'NotSet'}' '          # NotSet
  379. echo ${VarNull}                         # только перевод строки
  380. echo -n ${VarNull:-'NotSet'}' '         # NotSet
  381. echo ${VarNull}                         # только перевод строки
  382. echo '- "пустая" переменная -'
  383. echo -n ${VarEmpty-'Empty'}' '          # только пробел
  384. echo ${VarEmpty}                        # только перевод строки
  385. echo -n ${VarEmpty:-'Empty'}' '         # Empty
  386. echo ${VarEmpty}                        # только перевод строки
  387. echo '- непустая переменная -'
  388. echo ${VarSomething-'Content'}          # Literal
  389. echo ${VarSomething:-'Content'}         # Literal
  390. echo '- Разреженный массив -'
  391. echo ${ArrayVar[@]-'not set'}
  392. # ASCII-Art time
  393. # State     Y==yes, N==no
  394. #           -       :-
  395. # Unset     Y       Y       ${# ... } == 0
  396. # Empty     N       Y       ${# ... } == 0
  397. # Contents  N       N       ${# ... } > 0
  398. #  Либо первая, либо вторая часть операции подстановки параметра по-умолчанию
  399. #+ может быть командой или вызовом функции.
  400. echo
  401. echo '- - Тест 1, не определенная переменная - -'
  402. declare -i t
  403. _decT() {
  404.     t=$t-1
  405. }
  406. # Для не определенной переменной: t == -1
  407. t=${#VarNull}                           # t == 0
  408. ${VarNull- _decT }                      # Вызов функции, теперь t == -1.
  409. echo $t
  410. # "Пустая" переменная: t == 0
  411. t=${#VarEmpty}                          # t == 0
  412. ${VarEmpty- _decT }                     # Функция _decT НЕ вызывается.
  413. echo $t
  414. # Непустая переменная: t == число непустых символов
  415. VarSomething='_simple'                  # Записать имя функции в переменную.
  416. t=${#VarSomething}                      # ненулевая длина
  417. ${VarSomething- _decT }                 # Вызывается функция _simple.
  418. echo $t                                 # Обратите внимание на вывод.
  419. # Упражнение: Разберитесь в этом примере.
  420. unset t
  421. unset _decT
  422. VarSomething=Literal
  423. echo
  424. echo '- - Тест (с изменением содержимого переменной) - -'
  425. echo '- Присвоить, если переменная не определена -'
  426. echo -n ${VarNull='NotSet'}' '          # NotSet NotSet
  427. echo ${VarNull}
  428. unset VarNull
  429. echo '- Присвоить, если переменная не определена -'
  430. echo -n ${VarNull:='NotSet'}' '         # NotSet NotSet
  431. echo ${VarNull}
  432. unset VarNull
  433. echo '- Не присваивать, если переменная пуста -'
  434. echo -n ${VarEmpty='Empty'}' '          # только пробел
  435. echo ${VarEmpty}
  436. VarEmpty=''
  437. echo '- Присвоить, если переменная пуста -'
  438. echo -n ${VarEmpty:='Empty'}' '         # Empty Empty
  439. echo ${VarEmpty}
  440. VarEmpty=''
  441. echo '- Не изменять, если переменная не пуста -'
  442. echo ${VarSomething='Content'}          # Literal
  443. echo ${VarSomething:='Content'}         # Literal
  444. # "Разреженные" Bash-Массивы
  445. ###
  446. #  Bash-Массивы не содержат пустых элементов, и индексация их,
  447. #+ если не оговорено иное, начинается с нуля.
  448. ###
  449. #  Инициализируем массив ArraySparse как раз тем способом,
  450. #+ когда "оговорено иное".  Ниже приведен один из вариантов:
  451. ###
  452. echo
  453. declare -a ArraySparse
  454. ArraySparse=( [1]=one [2]='' [4]='four' )
  455. # [0]=нет элемента, [2]=пустой элемент, [3]=нет элемента
  456. echo '- - Разреженный массив - -'
  457. # В двойных кавычках, значение IFS — по-умолчанию, Все-Элементы-Для
  458. IFS=$'\x20'$'\x09'$'\x0A'
  459. printf %q "${ArraySparse[*]}"
  460. echo
  461. #  Обратите внимание на отсутствие различий в том, как выводятся отсутствующий
  462. #+ и пустой элементы массива.
  463. #  Оба выводятся как экранированные пробелы.
  464. ###
  465. #  Не упустите из виду и то, что все неопределенные элементы массива,
  466. #+ предшествующие первому инициализированному элементу, не выводятся
  467. ###
  468. # Замечание о таком "поведении" Bash, версий 2.04, 2.05a и 2.05b,
  469. #+ было передано разработчикам has been reported
  470. #+ и возможно будет изменено в последующих версиях Bash.
  471. #  Чтобы вывести содержимое такого разреженного массива без изменений
  472. #+ требуется некоторое количество усилий.
  473. #  Вот один из возможных вариантов вывода такого массива:
  474. ###
  475. # local l=${#ArraySparse[@]}        # Количество инициализированных элементов
  476. # local f=0                         # Количество найденных индексов
  477. # local i=0                         # текущий индекс
  478. (                                   # Анонимная функция
  479.     for (( l=${#ArraySparse[@]}, f = 0, i = 0 ; f < l ; i++ ))
  480.     do
  481.         # 'if defined then...'
  482.         ${ArraySparse[$i]+ eval echo '\ ['$i']='${ArraySparse[$i]} ; (( f++ )) }
  483.     done
  484. )
  485. #  Важно:
  486. #  Команда "read -a array_name" начинает заполнять массив
  487. #+ array_name с нулевого элемента.
  488. #  ArraySparse — не содержит нулевого элемента.
  489. ###
  490. #  Для выполнения операций над разреженными массивами,
  491. #+ такими как чтение/запись массива из/в файла
  492. #+ программист должен сам создать программный код, который
  493. #+ будет удовлетворять его потребности.
  494. ###
  495. # Упражнение: разберитесь в следующем примере самостоятельно.
  496. unset ArraySparse
  497. echo
  498. echo '- - Замена по условию (замена не производится)- -'
  499. echo '- Не изменять если переменная не определена -'
  500. echo -n ${VarNull+'NotSet'}' '
  501. echo ${VarNull}
  502. unset VarNull
  503. echo '- Не изменять если переменная не определена -'
  504. echo -n ${VarNull:+'NotSet'}' '
  505. echo ${VarNull}
  506. unset VarNull
  507. echo '- Изменить если переменная пуста -'
  508. echo -n ${VarEmpty+'Empty'}' '              # Empty
  509. echo ${VarEmpty}
  510. VarEmpty=''
  511. echo '- Не изменять если переменная пуста -'
  512. echo -n ${VarEmpty:+'Empty'}' '             # Только пробел
  513. echo ${VarEmpty}
  514. VarEmpty=''
  515. echo '- Изменить, если переменная не пуста -'
  516. echo -n ${VarSomething+'Content'}' '        # Content Literal
  517. echo ${VarSomething}
  518. # Вызов функции
  519. echo -n ${VarSomething:+ $(_simple) }' '    # SimpleFunc Literal
  520. echo ${VarSomething}
  521. echo
  522. echo '- - Разреженный массив - -'
  523. echo ${ArrayVar[@]+'Empty'}                 # An array of 'Empty'(ies)
  524. echo
  525. echo '- - Тест 2, неопределенные переменные - -'
  526. declare -i t
  527. _incT() {
  528.     t=$t+1
  529. }
  530. #  Обратите внимание:
  531. #  Тот же самый тест, что и в случае с разреженными массивами
  532. # Неопределенная переменная: t == -1
  533. t=${#VarNull}-1                     # t == -1
  534. ${VarNull+ _incT }                  # Функция не вызывается.
  535. echo $t' Переменная не определена'
  536. # Пустая переменная: t == 0
  537. t=${#VarEmpty}-1                    # t == -1
  538. ${VarEmpty+ _incT }                 # Вызов функции.
  539. echo $t'  Переменная пуста'
  540. # Переменная не пуста: t == (количество непустых символов)
  541. t=${#VarSomething}-1                # количество_непустых_символов минус один
  542. ${VarSomething+ _incT }             # Вызов функции.
  543. echo $t'  Переменная не пуста'
  544. # Операции над элементами массива
  545. # -------------------------------
  546. echo
  547. echo '- - Выборка элементов - -'
  548. #  Строки, массивы и позиционные параметры
  549. #  Чтобы увидеть работу сценария с позиционными параметрами,
  550. #+ вызовите его с несколькими аргументами
  551. echo '- Все -'
  552. echo ${VarSomething:0}              # все не пустые символы
  553. echo ${ArrayVar[@]:0}               # все не пустые элементы
  554. echo ${@:0}                         # все не пустые параметры;
  555.                                     # параметр [0] игнорируется
  556. echo
  557. echo '- Все после -'
  558. echo ${VarSomething:1}              # все не пустые символы, стоящие после [0]
  559. echo ${ArrayVar[@]:1}               # все не пустые элементы, стоящие после [0]
  560. echo ${@:2}                         # все не пустые параметры, стоящие после [1]
  561. echo
  562. echo '- Диапазон символов -'
  563. echo ${VarSomething:4:3}            # ral
  564.                                     # Три символа, следующие
  565.                                     # за символом [3]
  566. echo '- Разреженный массив -'
  567. echo ${ArrayVar[@]:1:2}     #  four - Единственный непустой элемент.
  568. #  Чтобы убедиться в том, что Bash в данной ситуации рассматривает только
  569. #+ непустые элементы
  570. #  printf %q "${ArrayVar[@]:0:3}"    # Попробуйте раскомментарить эту строку
  571. #  Версии Bash 2.04, 2.05a и 2.05b,
  572. #+ работают с разреженными массивами не так как ожидается.
  573. #
  574. #  Chet Ramey обещал исправить это в последующих версиях Bash.
  575. echo '- Неразреженный массив -'
  576. echo ${@:2:2}               # За параметром [1] следуют еще два параметра
  577. # Простые примеры со строками и массивами строк:
  578. stringZ=abcABC123ABCabc
  579. arrayZ=( abcabc ABCABC 123123 ABCABC abcabc )
  580. sparseZ=( [1]='abcabc' [3]='ABCABC' [4]='' [5]='123123' )
  581. echo
  582. echo ' - - Простая строка - -'$stringZ'- - '
  583. echo ' - - Простой массив - -'${arrayZ[@]}'- - '
  584. echo ' - - Разреженный массив - -'${sparseZ[@]}'- - '
  585. echo ' - [0]==нет элемента, [2]==нет элемента, [4]==пустой элемент - '
  586. echo ' - [1]=abcabc [3]=ABCABC [5]=123123 - '
  587. echo ' - количество инициализированных элементов: '${#sparseZ[@]}
  588. echo
  589. echo '- - Префиксы - -'
  590. echo '- - Шаблон должен совпадать с первым символом строки. - -'
  591. echo '- - Шаблон может быть строкой или результатом работы функции. - -'
  592. echo
  593. # Функция, результатом работы которой является обычная строка
  594. _abc() {
  595.     echo -n 'abc'
  596. }
  597. echo '- Кратчайший префикс -'
  598. echo ${stringZ#123}                 # Без изменения — не префикс.
  599. echo ${stringZ#$(_abc)}             # ABC123ABCabc
  600. echo ${arrayZ[@]#abc}               # Применяется к каждому элементу массива.
  601. # Chet Ramey обещал исправить в последующих версиях Bash.
  602. # echo ${sparseZ[@]#abc}            # В версии 2.05b — core dumps.
  603. # -Это было бы здорово- First-Subscript-Of (Первый-Индекс-Массива)
  604. # echo ${#sparseZ[@]#*}             #  line 805: ${#sparseZ[@]#*}: bad substitution.
  605. echo
  606. echo '- Наибольший префикс -'
  607. echo ${stringZ##1*3}                # Без изменения — не префикс.
  608. echo ${stringZ##a*C}                # abc
  609. echo ${arrayZ[@]##a*c}              # ABCABC 123123 ABCABC
  610. # Chet Ramey обещал исправить в последующих версиях Bash.
  611. # echo ${sparseZ[@]##a*c}           # В версии 2.05b — core dumps.
  612. echo
  613. echo '- - Суффиксы - -'
  614. echo '- - Шаблон должен совпадать с последним символом строки. - -'
  615. echo '- - Шаблон может быть строкой или результатом работы функции. - -'
  616. echo
  617. echo '- Кратчайший суффикс -'
  618. echo ${stringZ%1*3}                 # Без изменения — не суффикс.
  619. echo ${stringZ%$(_abc)}             # abcABC123ABC
  620. echo ${arrayZ[@]%abc}               # Применяется к каждому элементу массива.
  621. # Chet Ramey обещал исправить в последующих версиях Bash.
  622. # echo ${sparseZ[@]%abc}            # В версии 2.05b — core dumps.
  623. # -Это было бы здорово- Last-Subscript-Of (Последний-Индекс-Массива)
  624. # echo ${#sparseZ[@]%*}             #  line 830: ${#sparseZ[@]%*}: bad substitution
  625. echo
  626. echo '- Наибольший суффикс -'
  627. echo ${stringZ%%1*3}                # Без изменения — не суффикс.
  628. echo ${stringZ%%b*c}                # a
  629. echo ${arrayZ[@]%%b*c}              # a ABCABC 123123 ABCABC a
  630. # Chet Ramey обещал исправить в последующих версиях Bash.
  631. # echo ${sparseZ[@]%%b*c}           # В версии 2.05b — core dumps.
  632. echo
  633. echo '- - Замена подстроки - -'
  634. echo '- - Подстрока может находиться в любом месте в строке. - -'
  635. echo '- - Первый описатель задает шаблон поиска - -'
  636. echo '- - Шаблон может быть строкой или результатом работы функции. - -'
  637. echo '- - Второй описатель может быть строкой или результатом работы функции. - -'
  638. echo '- - Второй описатель может быть опущен. В результате получится:'
  639. echo '    Заменить-Ничем (Удалить) - -'
  640. echo
  641. # Функция, результатом работы которой является обычная строка
  642. _123() {
  643.     echo -n '123'
  644. }
  645. echo '- Замена первого вхождения -'
  646. echo ${stringZ/$(_123)/999}         # Подстрока 123 заменена на 999).
  647. echo ${stringZ/ABC/xyz}             # xyzABC123ABCabc
  648. echo ${arrayZ[@]/ABC/xyz}           # Применяется ко всем элементам массива.
  649. echo ${sparseZ[@]/ABC/xyz}          # Работает так как и ожидается.
  650. echo
  651. echo '- Удаление первого вхождения -'
  652. echo ${stringZ/$(_123)/}
  653. echo ${stringZ/ABC/}
  654. echo ${arrayZ[@]/ABC/}
  655. echo ${sparseZ[@]/ABC/}
  656. #  Замещающий элемент необязательно должен быть строкой,
  657. #+ допускается употреблять результат функции.
  658. #  Это применимо ко всем формам замены.
  659. echo
  660. echo '- Замена первого вхождения результатом работы функции -'
  661. echo ${stringZ/$(_123)/$(_simple)}  # Работает так как и ожидается.
  662. echo ${arrayZ[@]/ca/$(_simple)}     # Применяется ко всем элементам массива.
  663. echo ${sparseZ[@]/ca/$(_simple)}    # Работает так как и ожидается.
  664. echo
  665. echo '- Замена всех вхождений -'
  666. echo ${stringZ//[b2]/X}             # все символы b и 2 заменяются символом X
  667. echo ${stringZ//abc/xyz}            # xyzABC123ABCxyz
  668. echo ${arrayZ[@]//abc/xyz}          # Применяется ко всем элементам массива.
  669. echo ${sparseZ[@]//abc/xyz}         # Работает так как и ожидается.
  670. echo
  671. echo '- Удаление всех вхождений -'
  672. echo ${stringZ//[b2]/}
  673. echo ${stringZ//abc/}
  674. echo ${arrayZ[@]//abc/}
  675. echo ${sparseZ[@]//abc/}
  676. echo
  677. echo '- - Замена префикса - -'
  678. echo '- - Шаблон должен совпадать с первым символом строки. - -'
  679. echo
  680. echo '- - Замена префикса - -'
  681. echo ${stringZ/#[b2]/X}             # Без изменения — не префикс.
  682. echo ${stringZ/#$(_abc)/XYZ}        # XYZABC123ABCabc
  683. echo ${arrayZ[@]/#abc/XYZ}          # Применяется ко всем элементам массива.
  684. echo ${sparseZ[@]/#abc/XYZ}         # Работает так как и ожидается.
  685. echo
  686. echo '- Удаление префикса -'
  687. echo ${stringZ/#[b2]/}
  688. echo ${stringZ/#$(_abc)/}
  689. echo ${arrayZ[@]/#abc/}
  690. echo ${sparseZ[@]/#abc/}
  691. echo
  692. echo '- - Замена суффикса - -'
  693. echo '- - Шаблон должен совпадать с последним символом строки. - -'
  694. echo
  695. echo '- - Замена суффикса - -'
  696. echo ${stringZ/%[b2]/X}             # Без изменения — не суффикс.
  697. echo ${stringZ/%$(_abc)/XYZ}        # abcABC123ABCXYZ
  698. echo ${arrayZ[@]/%abc/XYZ}          # Применяется ко всем элементам массива.
  699. echo ${sparseZ[@]/%abc/XYZ}         # Работает так как и ожидается.
  700. echo
  701. echo '- Удаление суффикса -'
  702. echo ${stringZ/%[b2]/}
  703. echo ${stringZ/%$(_abc)/}
  704. echo ${arrayZ[@]/%abc/}
  705. echo ${sparseZ[@]/%abc/}
  706. echo
  707. echo '- - Специальный случай — пустой шаблон поиска - -'
  708. echo
  709. echo '- Префикс -'
  710. # пустой шаблон означает простую вставку в начало строки
  711. echo ${stringZ/#/NEW}               # NEWabcABC123ABCabc
  712. echo ${arrayZ[@]/#/NEW}             # Применяется ко всем элементам массива.
  713. echo ${sparseZ[@]/#/NEW}            # И к неинициализированным элементам тоже
  714. echo
  715. echo '- Суффикс -'
  716. # пустой шаблон означает простое добавление в конец строки
  717. echo ${stringZ/%/NEW}               # abcABC123ABCabcNEW
  718. echo ${arrayZ[@]/%/NEW}             # Применяется ко всем элементам массива.
  719. echo ${sparseZ[@]/%/NEW}            # И к неинициализированным элементам тоже
  720. echo
  721. echo '- - Специальный случай оператора For-Each - -'
  722. echo '- - - - This is a nice-to-have dream - - - -'
  723. echo
  724. _GenFunc() {
  725.     echo -n ${0}                    # Только как иллюстрация.
  726.     # Фактически здесь может стоять любое другое выражение.
  727. }
  728. # Все вхождения, совпадающие с шаблоном "*"
  729. # В настоящее время, шаблон //*/ не совпадает с пустыми
  730. #+ и неинициализированными элементами.
  731. # В то время как шаблоны /#/ и /%/ совпадают с пустыми и не совпадают
  732. #+ с неинициализированными элементами.
  733. echo ${sparseZ[@]//*/$(_GenFunc)}
  734. exit 0