Стратегия передачи аргументов - переменные среды и командная строка
Большинство приложений, которые мы пишем разработчикам, должны быть внешне параметризованы при запуске. Мы передаем пути к файлам, имена каналов, адреса TCP/IP и т. Д. До сих пор я использовал командную строку, чтобы передать их запущенному приложению. Мне пришлось разбирать командную строку в main
и направьте аргументы туда, где они нужны, что, конечно, является хорошим дизайном, но его трудно поддерживать для большого количества аргументов. Недавно я решил использовать механизм переменных среды. Они глобальны и доступны из любого места, что менее изящно с архитектурной точки зрения, но ограничивает объем кода.
Это мои первые (и, возможно, довольно поверхностные) впечатления от обеих стратегий, но я хотел бы услышать мнения более опытных разработчиков. Каковы преимущества и недостатки использования переменных среды и аргументов командной строки для передачи аргументов процессу? Я хотел бы принять во внимание следующие вопросы:
- качество дизайна (гибкость / ремонтопригодность),
- ограничения памяти,
- Переносимость решения.
Примечания:
Объявление. 1. Это основной аспект, который меня интересует.
Объявление. 2. Это немного прагматично. Я знаю о некоторых ограничениях для Windows, которые в настоящее время огромны (более 32 КБ как для командной строки, так и для блока среды). Я думаю, что это не проблема, так как вы просто должны использовать файл для передачи тонны аргументов, если вам нужно.
Объявление. 3. Я почти ничего не знаю о Unix, поэтому я не уверен, что обе стратегии так же удобны, как в Windows. Уточните это, если хотите.
4 ответа
1) Я бы рекомендовал избегать переменных среды в максимально возможной степени.
Плюсы переменных среды
- прост в использовании, потому что они видны из любого места. Если много независимых программ нуждаются в информации, такой подход намного удобнее.
Минусы переменных среды
- трудно использовать правильно, потому что они видны (удаляются, устанавливаются) из любого места. Если я установлю новую программу, основанную на переменных среды, собираются ли они растоптать мои существующие? Случайно ли я испортил свои переменные окружения, когда я вчера занимался обезьянами?
Мое мнение
- используйте аргументы командной строки для тех аргументов, которые, скорее всего, будут разными для каждого отдельного вызова программы (т. е. n для программы, которая вычисляет n!)
- использовать конфигурационные файлы для аргументов, которые пользователь может разумно изменить, но не очень часто (например, отображать размер при всплывающем окне)
- используйте переменные окружения экономно - желательно только для аргументов, которые, как ожидается, не изменятся (т. е. местоположение интерпретатора Python)
- ваша точка зрения
They are global and accessible from anywhere, which is less elegant from architectural point of view, but limits the amount of code
напоминает мне обоснования использования глобальных переменных;)
Мои шрамы от непосредственного переживания ужасов чрезмерного использования переменных окружающей среды
- две программы, которые нам нужны на работе, которые не могут работать на одном компьютере одновременно из-за столкновений с окружающей средой
- Несколько версий программ с одним и тем же именем, но с разными ошибками - поставили целый семинар на колени на несколько часов, потому что местоположение программы было извлечено из среды и было (молча, тонко) неверно.
2) Пределы
Если бы я раздвигал границы того, что может содержать командная строка, или того, что может обрабатывать среда, я бы немедленно осуществил рефакторинг.
В прошлом я использовал JSON для приложения командной строки, которому требовалось много параметров. Было очень удобно иметь возможность использовать словари и списки, а также строки и цифры. Приложение заняло всего пару аргументов командной строки, одним из которых было расположение файла JSON.
Преимущества такого подхода
- не нужно было писать много (болезненного) кода для взаимодействия с библиотекой CLI - может быть трудно заставить многие общие библиотеки применять сложные ограничения (под "сложным" я подразумеваю более сложный, чем проверка на наличие конкретный ключ или чередование набора ключей)
- не беспокойтесь о требованиях библиотек CLI к порядку аргументов - просто используйте объект JSON!
- легко представить сложные данные (отвечая
What won't fit into command line parameters?
) такие как списки - простота использования данных из других приложений - как для создания, так и для программного анализа
- легко приспособить будущие расширения
Примечание: я хочу отличить это от подхода.config-file - это не для хранения пользовательской конфигурации. Возможно, мне следует назвать это подходом "файл параметров командной строки", потому что я использую его для программы, которая нуждается в большом количестве значений, которые плохо помещаются в командной строке.
3) Переносимость решения: я не очень много знаю о различиях между Mac, PC и Linux в отношении переменных среды и аргументов командной строки, но могу вам сказать:
- все три имеют поддержку переменных среды
- все они поддерживают аргументы командной строки
Да, я знаю - это было не очень полезно. Мне жаль. Но ключевым моментом является то, что вы можете ожидать, что разумное решение будет переносимым, хотя вы определенно захотите проверить это для своих программ (например, являются ли аргументы командной строки чувствительными к регистру на любых платформах? На всех платформах? Я не знаю).
Последний пункт:
Как упоминал Томаш, для большинства приложений не имеет значения, откуда берутся параметры.
Вы должны абстрагировать параметры чтения, используя шаблон Стратегии. Создать абстракцию с именем ConfigurationSource
имеющий readConfig(key) -> value
метод (или возвращая некоторые Configuration
объект / структура) со следующими реализациями:
CommandLineConfigurationSource
EnvironmentVariableConfigurationSource
WindowsFileConfigurationSource
- загрузка из файла конфигурации изC:/Document and settings...
WindowsRegistryConfigurationSource
NetworkConfigrationSource
UnixFileConfigurationSource
- - загрузка из файла конфигурации из/home/user/...
DefaultConfigurationSource
- по умолчанию- ...
Вы также можете использовать шаблон цепочки ответственности для цепочки источников в различных конфигурациях, таких как: если аргумент командной строки не указан, попробуйте переменную окружения, а если ничего не помогает, верните значения по умолчанию.
Объявление 1. Этот подход позволяет не только абстрагировать конфигурацию чтения, но и легко изменять базовый механизм без какого-либо влияния на код клиента. Также вы можете использовать несколько источников одновременно, откатываясь или собирая конфигурацию из разных источников.
Объявление 2. Просто выберите подходящую реализацию. Конечно, некоторые записи конфигурации не вписываются, например, в аргументы командной строки.
Объявление 3. Если некоторые реализации не переносимы, имейте две, одну молча игнорируют / пропускают, когда они не подходят для данной системы.
Я думаю, что на этот вопрос уже достаточно хорошо ответили, но я чувствую, что он заслуживает обновления 2018 года. Мне кажется, что неизменная выгода переменных среды заключается в том, что для работы с ними обычно требуется меньше кода котельной плиты. Это делает код более понятным для чтения. Однако основной недостаток заключается в том, что они удаляют слои изоляции от разных приложений, работающих на одном компьютере. Я думаю, что именно здесь Docker действительно сияет. Мой любимый шаблон проектирования - использовать только переменные среды и запускать приложение внутри контейнера Docker. Это устраняет проблему изоляции.
Я в целом согласен с предыдущими ответами, но есть еще один важный аспект: удобство использования.
Например, вы можете создать репозиторий с каталогом .git вне его. Чтобы указать это, вы можете использовать аргумент командной строки
Конечно, если вы измените текущий каталог на другой репозиторий или унаследуете переменные окружения в скриптах, вы получите ошибку. Но если вам нужно ввести несколько
Другой пример. Кажется, что у него даже нет партнера по командной строке (однако,
В общем, использование аргументов командной строки или окружения одинаково просто в UNIX: можно использовать аргумент командной строки
$ command --arg=myarg
или переменная окружения в одной строке:
$ ARG=myarg command
Также легко захватить аргументы командной строки в
alias cfg='git --git-dir=$HOME/.cfg/ --work-tree=$HOME' # for dotfiles
alias grep='grep --color=auto'
Обычно большинство аргументов передаются через командную строку. Я согласен с предыдущими ответами, что это более функционально и прямолинейно, и что переменные окружения в сценариях похожи на глобальные переменные в программах.
GNU libc говорит следующее:
Механизм argv обычно используется для передачи аргументов командной строки, характерных для конкретной вызываемой программы. Окружающая среда, с другой стороны, отслеживает информацию, которая используется многими программами, редко изменяется и реже используется.
Помимо того, что было сказано об опасностях переменных среды, есть хорошие примеры их использования. GNU make имеет очень гибкую обработку переменных среды (и, таким образом, хорошо интегрирован с оболочкой):
Каждая переменная среды, которую make видит при запуске, преобразуется в переменную make с тем же именем и значением. Однако явное назначение в make-файле или с аргументом команды переопределяет среду. (-- и есть возможность изменить это поведение) ...
Таким образом, установив переменную CFLAGS в вашей среде, вы можете заставить все компиляции C в большинстве make-файлов использовать предпочитаемые вами параметры компилятора. Это безопасно для переменных со стандартными или обычными значениями, потому что вы знаете, что ни один make-файл не будет использовать их для других целей.
Наконец, подчеркну, что для программы важнее всего не программист, а пользовательский опыт. Возможно, вы включили это в аспект дизайна , но внутренний и внешний дизайн — это совершенно разные сущности.
И несколько слов о аспектах программирования. Вы не написали, какой язык вы используете, но давайте представим, что ваши инструменты позволяют вам наилучший анализ аргументов. В Python я использую , очень гибкий и многофункциональный. Чтобы получить проанализированные аргументы, можно использовать команду вида
args = parser.parse_args()
args можно дополнительно разделить на проанализированные аргументы (скажем, args.my_option), но я также могу передать их целиком в свою функцию. Это решение абсолютно не «трудно поддерживать для большого количества аргументов» (если ваш язык это позволяет). Действительно, если у вас много параметров и они не используются при разборе аргументов, передавайте их в контейнере до конечного пункта назначения и избегайте дублирования кода (что приводит к негибкости).
И самое последнее замечание: гораздо проще анализировать переменные окружения, чем аргументы командной строки. Переменная окружения — это просто пара,
И еще кое-что. Твое беспокойство по поводу памяти немного тревожит. Не пишите слишком общие программы. Библиотека должна быть гибкой, но хорошая программа полезна и без аргументов. Если вам нужно передать много, это, вероятно , данные , а не аргументы . Как считывать данные в программу — гораздо более общий вопрос, не имеющий единого решения для всех случаев.