Использовать конфигурационный файл для моего сценария оболочки

Мне нужно создать файл конфигурации для моего собственного скрипта: вот пример:

сценарий:

  • Синтаксическая ошибка при экспликации сценария оболочки bash из busybox
  • Невозможно суммировать числа, полученные от stdin, используя bc
  • Переключение источника и адресата (или отмена операции mv, cp)
  • bash эквивалент zsh $ @
  • Как создать объединенный вход (stdin) для программы
  • Сопоставьте строки файла с заголовками в другом, чтобы получить полный параграф
  • #!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2 

    Содержимое /home/myuser/test/config :

     nam="Mark" sur="Brown" 

    это работает!

    Мой вопрос: это правильный способ сделать это или есть другие способы?

  • Как добавить цвет фона с непрямоугольной формой в строку подсказки (PS1)?
  • Автозаполнение Bash для переменной среды, содержащей имя каталога
  • Как выполнять очистку имен файлов, содержащих недопустимые символы
  • Как создать diff для серии файлов?
  • Горячие клавиши Ch, Cm в bash
  • Переменная в Bash, содержащая кавычки и пробелы
  • 6 Solutions collect form web for “Использовать конфигурационный файл для моего сценария оболочки”

    source не защищен, так как он будет выполнять произвольный код. Это может не беспокоить вас, но если права доступа к файлам неверны, злоумышленник может получить доступ к файловой системе для выполнения кода как привилегированного пользователя, введя код в файл конфигурации, загруженный иным образом защищенным скриптом, например, init.

    До сих пор лучшим решением, которое я смог определить, является неуклюжие решения для создания новых решений:

    myscript.conf

     password=bar echo rm -rf / PROMPT_COMMAND='echo "Sending your last command $(history 1) to my email"' hostname=localhost; echo rm -rf / 

    Используя source , это будет запускать echo rm -rf / дважды, а также изменить $PROMPT_COMMAND . Вместо этого сделайте следующее:

    myscript.sh (Bash 4)

     #!/bin/bash typeset -A config # init array config=( # set default values in config array [username]="root" [password]="" [hostname]="localhost" ) while read line do if echo $line | grep -F = &>/dev/null then varname=$(echo "$line" | cut -d '=' -f 1) config[$varname]=$(echo "$line" | cut -d '=' -f 2-) fi done < myscript.conf echo ${config[username]} # should be loaded from defaults echo ${config[password]} # should be loaded from config file echo ${config[hostname]} # includes the "injected" code, but it's fine here echo ${config[PROMPT_COMMAND]} # also respects variables that you may not have # been looking for, but they're sandboxed inside the $config array 

    myscript.sh (совместим с Mac / Bash 3)

     #!/bin/bash config() { val=$(grep -E "^$1=" myscript.conf 2>/dev/null || echo "$1=__DEFAULT__" | head -n 1 | cut -d '=' -f 2-) if [[ $val == __DEFAULT__ ]] then case $1 in username) echo -n "root" ;; password) echo -n "" ;; hostname) echo -n "localhost" ;; esac else echo -n $val fi } echo $(config username) # should be loaded from defaults echo $(config password) # should be loaded from config file echo $(config hostname) # includes the "injected" code, but it's fine here echo $(config PROMPT_COMMAND) # also respects variables that you may not have # been looking for, but they're sandboxed inside the $config array 

    Надеюсь, это поможет. Ответьте, если в моем коде обнаружен эксплойт безопасности.

    Вот чистая и портативная версия, совместимая с Bash 3 и выше, как на Mac, так и на Linux.

    Он задает все значения по умолчанию в отдельном файле, чтобы избежать необходимости в огромной, загроможденной дублированной настройке конфигурации по умолчанию во всех ваших сценариях оболочки. И он позволяет вам выбирать между чтением с резервами по умолчанию или без них:

    config.cfg :

     myvar=Hello World 

    config.cfg.defaults :

     myvar=Default Value othervar=Another Variable 

    config.shlib (это библиотека, поэтому нет строки shebang):

     config_read_file() { (grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d '=' -f 2-; } config_get() { val="$(config_read_file config.cfg "${1}")"; if [ "${val}" = "__UNDEFINED__" ]; then val="$(config_read_file config.cfg.defaults "${1}")"; fi printf -- "%s" "${val}"; } 

    test.sh (или любые скрипты, где вы хотите прочитать значения конфигурации) :

     #!/usr/bin/env bash source config.shlib; # load the config library functions echo "$(config_get myvar)"; # will be found in user-cfg printf -- "%s\n" "$(config_get myvar)"; # safer way of echoing! myvar="$(config_get myvar)"; # how to just read a value without echoing echo "$(config_get othervar)"; # will fall back to defaults echo "$(config_get bleh)"; # "__UNDEFINED__" since it isn't set anywhere 

    Объяснение тестового скрипта:

    • Обратите внимание, что все использования config_get в test.sh завернуты в двойные кавычки. Упаковывая каждый config_get в двойные кавычки, мы гарантируем, что текст в значении переменной никогда не будет неверно истолкован как флаги. И это гарантирует, что мы сохраним пробелы должным образом, например, несколько пробелов в строке в значении конфигурации.
    • И что это за строка printf ? Ну, это то, о чем вам следует знать: echo – это плохая команда для печати текста, который у вас нет. Даже если вы используете двойные кавычки, он будет интерпретировать флаги. Попробуйте установить myvarconfig.cfg ) на -e и вы увидите пустую строку, потому что echo будет считать, что это флаг. Но printf не имеет этой проблемы. printf -- говорит «напечатайте это и не интерпретируйте ничего как флаги», а "%s\n" говорит «форматирует вывод как строку с завершающей новой строкой, и, наконец, конечным параметром является значение для printf для форматирования.
    • Если вы не собираетесь отображать значения на экране, вы просто назначаете их обычно, например myvar="$(config_get myvar)"; , Если вы собираетесь распечатать их на экране, я предлагаю использовать printf для полной защиты от любых эхо-несовместимых строк, которые могут быть в пользовательской конфигурации. Но эхо прекрасно, если предоставленная пользователем переменная не является первым символом строки, которую вы эхом, так как это единственная ситуация, когда «флаги» могут быть интерпретированы, что-то вроде echo "foo: $(config_get myvar)"; безопасен, поскольку «foo» не начинается с тире и поэтому говорит эхо, что остальная часть строки также не является флагом для него. 🙂

    Наиболее распространенным, эффективным и правильным способом является использование source или . как сокращенная форма. Например:

     source /home/myuser/test/config 

    или

     . /home/myuser/test/config 

    Однако следует учитывать проблемы безопасности, связанные с использованием дополнительного файла конфигурации из внешнего источника, с учетом того, что может быть вставлен дополнительный код. Для получения дополнительной информации, в том числе о том, как обнаружить и решить эту проблему, я бы рекомендовал взглянуть на раздел «Защитить его» в http://wiki.bash-hackers.org/howto/conffile#secure_it

    Разберите файл конфигурации, не выполняйте его.

    В настоящее время я пишу приложение на работе, которое использует чрезвычайно простую конфигурацию XML:

     <config> <username>username-or-email</username> <password>the-password</password> </config> 

    В сценарии оболочки («приложение») это то, что я делаю, чтобы получить имя пользователя (более или менее, я поместил его в функцию оболочки):

     username="$( xml sel -t -v '/config/username' "$config_file" )" 

    Команда xml – это XMLStarlet , доступная для большинства Unices.

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

    Если вы предпочитаете JSON, есть jq который является простым в использовании синтаксическим анализатором JSON.

    Мой конфигурационный файл будет выглядеть примерно так в JSON:

     { "username": "username-or-email", "password": "the-password" } 

    И тогда я получаю имя пользователя в скрипте:

     username="$( jq -r '.username' "$config_file" )" 

    По моему сценарию, source или . было хорошо, но я хотел поддерживать переменные локальной среды (т.е. FOO=bar myscript.sh ), имеющие приоритет над настроенными переменными. Я также хотел, чтобы файл конфигурации был доступен для редактирования и удобен для пользователя, используемого для получения файлов конфигурации, и чтобы он был как можно малым / простым, чтобы не отвлекать внимание от основного смысла моего очень маленького скрипта.

    Вот что я придумал:

     CONF=${XDG_CONFIG_HOME:-~/config}/myscript.sh if [ ! -f $CONF ]; then cat > $CONF << CONF VAR1="default value" CONF fi . <(sed 's/^\([^=]\+\) *= *\(.*\)$/\1=${\1:-\2}/' < $CONF) 

    По существу – он проверяет определения переменных (не будучи очень гибкими в отношении пробелов) и перезаписывает эти строки, чтобы значение было преобразовано в значение по умолчанию для этой переменной, а переменная не изменена, если найдена, например, переменная XDG_CONFIG_HOME выше. Это источник измененной версии файла конфигурации и продолжается.

    Будущая работа могла бы сделать sed скрипт более надежным, отфильтровать строки, которые выглядят странно или не являются определениями и т. Д., А не прерывать комментарии к концу строки, – но для меня это достаточно хорошо.

    Ты можешь это сделать:

     #!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF 
    Linux и Unix - лучшая ОС в мире.