Какой безопасный способ интерполировать переменные Bash в переменной heredoc / multi-line без использования eval? - PullRequest
0 голосов
/ 10 октября 2019

Итак, у меня возникла эта безумная идея встроить документацию в мои скрипты (что не является чем-то новым, просто новым для меня) и проанализировать их с помощью sed. Я добиваюсь этого, просто закомментировав блок текста с разделителями строк, а затем вытолкнув хеш комментария (в каждой строке), когда я печатаю строку в стандартный вывод.

В качестве примера представьте сценарий оболочки myscript.sh, подобный следующему:

   #!/usr/bin/env bash
   #---- doc:start ----#
   # Hello $User\n     
   # This is myscript.sh
   # ${red}This will be red!${eol}
   #---- doc:end   ----#
   printf "Hello, World!"

У меня есть сценарий sed, который захватывает весь контент между линейными дилометрами и помещает его в многострочную переменную $doc

С помощью этой переменной я хочу интерполировать любые локальные переменные (необязательно экспортируемые переменные), escape-коды, цвета терминов и т. Д., И, глядя на то, как это сделать, - единственный ответ, который мне удалосьfind использует eval;Вот рабочий пример (здесь используются строки):

  while IFS= read -r line; do
    line=$(eval "echo -e \"$line\""); 
    echo "$line"
  done  <<< "$doc"; # doc is a multiline variable

Однако, если кто-то добавит $(rm -rf ..) к этому блоку текста, он будет выполнен;похоже, что эти шаблоны допускают другие небезопасные сценарии.

Другой подход, на который я смотрел, - это динамическое назначение переменных с использованием printf -v line ..., но не удается заставить работать подстановку переменных.

Я бы хотел добиться этого в контексте Bash 3.2 без необходимости установки каких-либо дополнительных инструментов или утилит;POSIX, если это вообще возможно, но Bashisms и обычные утилиты тоже в порядке. Хотите, чтобы это работало как на Mac, так и на Ubuntu .

Возможно, сработает регулярное выражение для исключения всего, что выглядит как исполняемые команды (или иным образом супер-экранирование подозрительных строк)тоже.

Обновление: Ссылка на "рабочий" код

Ответы [ 2 ]

3 голосов
/ 10 октября 2019

Используйте envsubst из пакета gettext GNU.

Для этого требуется только экспортировать переменные, которые должны быть открыты для замены. Приведенное ниже использование демонстрирует временное превращение обычных переменных оболочки red и eol в переменные среды на время вызова envsubst:

red="$red" eol="$eol" envsubst <<<'EOF'
...${red}...${eol}...
EOF
0 голосов
/ 11 октября 2019

Похоже, что вам действительно нужно шаблонизировать, а не вставлять переменные. Тем не менее, вы можете сделать это явно переменная за переменной, что может обеспечить дополнительную безопасность.

#!/usr/bin/env bash
#---- doc:start ----#
# Hello $User\n
# This is myscript.sh
# ${red}This will be red!${eol}
#---- doc:end   ----#

# template replacemenmts
red=$'\e[31m'
norm=$'\e[m'
eol=$'\n'

# fetch and trim...
s=$(< $0)
s=${s%%#---- doc:end   ----#*}
s=${s##*#---- doc:start ----#}

# do substitutions
s=${s//\$User/$USER}
s=${s//\${red\}/$red}
s=${s//\${eol\}/$eol}
s=${s//\\n/$eol}

printf '%s\n%s' "$s" "$norm"

Это позволяет избежать использования sed или чего-либо еще, что может отличаться в зависимости от платформы к платформе. (sed отличается между GNU, FreeBSD / MacOS и Solaris, например.)

Просто для размышлений, вот пример того, как я склонен встраивать документацию в bashскрипты. (И да, я использую sed. :-))

...