Пример A-25. Предотвращение интерпретации строк символов

Пример A-25. Предотвращение интерпретации строк символов

  1. #! /bin/bash
  2. # protect_literal.sh
  3. # set -vx
  4. :<<-'_Protect_Literal_String_Doc'
  5.     Copyright (c) Michael S. Zick, 2003; All Rights Reserved
  6.     Ограничения: Допускается использовать без каких либо ограничений в любой форме.
  7.     Гарантии: Никаких
  8.     Издание: $ID$
  9.     Этот встроенный документ Bash отправит на устройство '/dev/null'.
  10.     (Раскомментарьте команду set, стоящую выше, чтобы убедиться в этом.)
  11.     Удалите первую строку (Sha-Bang), если вы собираетесь использовать этот сценарий
  12.     в качестве библиотеки.  Не забудьте при этом закомментарить примеры
  13.     использования процедур (там где это указано).
  14.     Порядок использования:
  15.         _protect_literal_str 'Whatever string meets your ${fancy}'
  16.         Какая бы строка ни была передана функции,
  17.         она просто будет выведена на stdout,
  18.         включая "строгие" кавычки.
  19.         $(_protect_literal_str 'Whatever string meets your ${fancy}')
  20.         как правосторонняя часть операции присваивания.
  21.     Назначение:
  22.         В операциях присваивания, предотвращают дополнительную
  23.         интерпретацию содержимого строки, путем добавления "строгих"
  24.         кавычек.
  25.     Примечание:
  26.         Имена функций (_*) выбраны таким образом, чтобы избежать
  27.         конфликтов имен, при подключении данного сценария
  28.         к пользовательским сценариям, в качестве библиотеки.
  29. _Protect_Literal_String_Doc
  30. _protect_literal_str() {
  31. # Выберем неиспользуемый, непечатный символ в качестве разделителя полей для IFS.
  32. # В этом нет необходимости, но делается это для демогстрации
  33. # того, что разделитель полей игнорируется.
  34.     local IFS=$'\x1B'               # Символ \ESC
  35. # Заключим Все-Элементы в "строгие" кавычки.
  36.     local tmp=$'\x27'$@$'\x27'
  37.     local len=${#tmp}       # Исключительно для демонстрации.
  38.     echo $tmp, длина:$len.  # Вывод строки и дополнительной информации.
  39. }
  40. # Версия с более коротким именем.
  41. _pls() {
  42.     local IFS=$'x1B'                # Символ \ESC (не обязательно)
  43.     echo $'\x27'$@$'\x27'           # Заключить в "строгие" кавычки
  44. }
  45. # :<<-'_Protect_Literal_String_Test'
  46. # # # Раскомментарьте вышестоящую строку, чтобы запретить исполнение нижеследующего кода. # # #
  47. # Посмотрим как выглядит простой вывод.
  48. echo
  49. echo "- - Тест #1 - -"
  50. _protect_literal_str 'Hello $user'
  51. _protect_literal_str 'Hello "${username}"'
  52. echo
  53. # В результате должно получиться:
  54. # - - Тест #1 - -
  55. # 'Hello $user', длина: 13.
  56. # 'Hello "${username}"', длина: 21.
  57. #  Собственно получили то, что и ожидали, тогда в чем проблема?
  58. #  Проблема скрыта внутри Bash, в порядке выполнения операций.
  59. #  Она проявляется, когда функции учавствуют в операциях присваивания.
  60. # Объявим массив тестовых значений.
  61. declare -a arrayZ
  62. # Запишем в массив элементы с разного рода кавычками и экранирующими символами.
  63. arrayZ=( zero "$(_pls 'Hello ${Me}')" 'Hello ${You}' "\'Pass: ${pw}\'" )
  64. # Теперь выведем массив на экран и посмотрим, что там лежит.
  65. echo "- - Тест #2 - -"
  66. for (( i=0 ; i<${#arrayZ[*]} ; i++ ))
  67. do
  68.     echo  Элемент $i: ${arrayZ[$i]}, длина: ${#arrayZ[$i]}.
  69. done
  70. echo
  71. # В результате должно получиться:
  72. # - - Тест #2 - -
  73. # Элемент 0: zero, длина: 4.           # Маркировочный (ничем не примечательный) элемент
  74. # Элемент 1: 'Hello ${Me}', длина: 13. # Результат "$(_pls '...' )"
  75. # Элемент 2: Hello ${You}, длина: 12.  # Кавычки исчезли
  76. # Элемент 3: \'Pass: \', длина: 10.    # ${pw} — была интерпретирована,
  77. #                                      # а на ее место подставлена пустая строка
  78. # Выполним присвоение одного массива другому.
  79. declare -a array2=( ${arrayZ[@]} )
  80. # И выведем его содержимое.
  81. echo "- - Тест #3 - -"
  82. for (( i=0 ; i<${#array2[*]} ; i++ ))
  83. do
  84.     echo  Элемент $i: ${array2[$i]}, длина: ${#array2[$i]}.
  85. done
  86. echo
  87. # В результате должно получиться:
  88. # - - Тест #3 - -
  89. # Элемент 0: zero, длина: 4.           # Наш маркер.
  90. # Элемент 1: Hello ${Me}, длина: 11.   # Вполне предсказуемый результат.
  91. # Элемент 2: Hello, длина: 5.          # ${You} — была интерпретирована.
  92. #                                      # а на ее место подставлена пустая строка
  93. # Элемент 3: 'Pass:, длина: 6.         # Элемент был "разбит" на два по пробелу.
  94. # Элемент 4: ', длина: 1.              # Завершающая кавычка попала в отдельный элемент.
  95. #  В Элементе 1 были удалены начальная и завершающая "строгие" кавычки.
  96. #  Хотя здесь и не показано, но начальные и звершающие пробелы также удаляются.
  97. #  Теперь, когда содержимое строки установлено, Bash всегда, внутри, будет
  98. #  "строго" окавычивать содержимое строки, на протяжении всей операции
  99. #  Зачем это нужно?
  100. #  В нашем случае, в конструкции "$(_pls 'Hello ${Me}')":
  101. #  " ... " -> Требуется интерпретация (экспансия), кавычки удаляются.
  102. #  $( ... ) -> Замещается результатом выполнения ..., пустая строка.
  103. #  _pls ' ... ' -> вызов функции со строковым аргументом, кавычки удаляются.
  104. #  Возвращаемый результат включает в себя "строгие" кавычки; НО обработка команды
  105. #+ уже была завершена выше, так что теперь они становятся частью присваиваемого
  106. #+ значения.
  107. #
  108. #  Таким образом, ${Me} оказывается частью результата
  109. #+ и сохраняется в первоначальном виде
  110. #  (До тех пор, пока явно не будет указано на необходимость ее интерпретации).
  111. #  Дополнительно: Взгляните, что произойдет, если в этих функциях
  112. #+ "строгие" кавычки ($'\x27') заменить на "мягкие" ($'\x22').
  113. #  Интересный результат получится если вообще убрать кавычки.
  114. # _Protect_Literal_String_Test
  115. # # # Раскомментарьте вышестоящую строку, чтобы запретить исполнение вышестоящего кода. # # #
  116. exit 0