совместимый с checkbashisms способ определения текущей оболочки

18

В моем .profileкоде я использую следующий код, чтобы гарантировать, что псевдонимы и функции, связанные с Bash, получаются только в том случае, если на самом деле оболочкой входа является Bash :

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

В настоящее время я нахожусь в процессе помещения файлов конфигурации, скриптов и функций моей оболочки под контроль версий. Я также недавно начал процесс удаления случайных Bashisms из сценариев оболочки, которые не извлекают выгоду из специфичных для Bash функций, например, заменяя function funcname()на funcname().

Для моего хранилища файлов оболочки я настроил ловушку перед фиксацией, которая запускает checkbashismsутилиту из пакета devscripts Debian для каждого shфайла в хранилище, чтобы не допустить непреднамеренного введения синтаксиса, специфичного для Bash. Тем не менее, это вызывает ошибку для моего .profile:

possible bashism in .profile line 51 ($BASH_SOMETHING):
if [ "${BASH_VERSION:-}" ]; then

Мне было интересно, если есть способ проверить, какая оболочка работает, что не вызовет предупреждение checkbashisms.

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

На данный момент я исключен .profileиз обработки checkbashisms; Это небольшой файл, так что проверить его несложно. Однако, изучив проблему, я все же хотел бы знать, существует ли POSIX-совместимый метод для определения, какая оболочка работает (или, по крайней мере, способ, который не приводит checkbashismsк сбою).


Дальнейшая справка / разъяснения

Одна из причин, по которой я настраиваю свои файлы конфигурации оболочки под контроль версий, заключается в том, чтобы настроить свою среду на всех системах, в которых я в данный момент регулярно выполняю вход: Cygwin, Ubuntu и CentOS (обе версии 5 и 7, с использованием Active Directory для пользователя. аутентификация). Я чаще всего захожу через X Windows / окружение рабочего стола и SSH для удаленных хостов. Тем не менее, я хотел бы, чтобы это было в будущем и как можно меньше зависело от системных зависимостей и других инструментов.

Я использовал checkbashismsв качестве простой автоматической проверки правильности синтаксиса моих файлов, связанных с оболочкой. Это не идеальный инструмент, например, я уже применил к нему патч, чтобы он не жаловался на использование command -vв моих скриптах. В ходе исследования я узнал, что настоящей целью программы является обеспечение соответствия политике Debian, которая, насколько я понимаю, основана на POSIX 2004, а не 2008 (или его редакции 2013 года).

Энтони Дж - справедливость для Моники
источник
То, что вы делаете, настолько же POSIX-совместимо, насколько это возможно, когда все дело в том, чтобы по-разному работать в разных POSIX-совместимых средах. Ваша говядина с чекбашизмом.
Жиль "ТАК - перестань быть злым"
И, кстати, я никогда не использовал чекбашизмы для проверки переносимости моей конфигурации. Я проверяю это, используя его в разных системах.
Жиль "ТАК - перестань быть злым"
2
И, между прочим, для конкретной вещи, которую вы делаете здесь, напишите, .bash_profileчто источники оба .profileи (условно) .bashrc.
Жиль "ТАК - перестань быть злым"
Спасибо за отзыв @ Жиль. Это очень элегантное решение для конкретной проблемы.
Энтони Дж. - справедливость для Моники

Ответы:

16

Ваш

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

код полностью соответствует POSIX и лучший способ проверить, что вы в данный момент работаете bash. Конечно, $BASH_VERSIONпеременная зависит от bash, но именно поэтому вы ее используете! Чтобы проверить, что ты бежишь bash!

Обратите внимание, что $BASH_VERSIONбудет установлено , будет ли bashвызываться как bashили sh. После того, как вы заявили, что работаете bash, вы можете использовать [ -o posix ]в качестве индикатора, что оболочка была вызвана как sh(хотя этот параметр также устанавливается, когда POSIXLY_CORRECT находится в среде или bashвызывается с помощью -o posix, или с SHELLOPTS = posix в среде. Но во всех этих случаях bashбудет вести себя как будто называется как sh).


Другая переменная, которую вы могли бы использовать вместо, $BASH_VERSIONи, checkbashismкажется, не будет жаловаться, если не передана -xопция is $BASH. Это также относится и к тому, bashчто вы также можете использовать, чтобы определить, бежите ли вы bashили нет.


Я также утверждаю, что это не совсем правильное использование checkbashisms. checkbashismsэто инструмент, помогающий вам писать переносимые shскрипты (согласно shспецификации в политике Debian, расширенный набор POSIX), он помогает идентифицировать нестандартный синтаксис, введенный людьми, пишущими скрипты в системах, где shесть символическая ссылка bash.

A .profileинтерпретируется различными оболочками, многие из которых не совместимы с POSIX. Как правило, вы не используете в shкачестве регистрационной оболочки, но скорлупа нравится zsh, fishили bashс более расширенными интерактивными возможностями.

bashи zshкогда не вызывается как shи когда их соответствующий файл сеанса профиля ( .bash_profile, .zprofile) не соответствует POSIX (особенно zsh), но все еще читается .profile.

Так что вам нужен не синтаксис POSIX, .profileа синтаксис, совместимый с POSIX (для sh), bashи zshесли вы когда-либо будете использовать эти оболочки (возможно, даже Bourne, поскольку оболочка Bourne также читает, .profileно обычно не встречается в системах на основе Linux) ).

checkbashismsопределенно поможет вам найти ошибки, но может не указывать синтаксис POSIX, который не совместим с zshили bash.

Здесь, если вы хотите использовать bashспецифичный для вас код (например, обойти эту bashошибку, при которой он не читает ~/.bashrcв интерактивных оболочках входа в систему), лучшим подходом было бы ~/.bash_profileсделать это (до или после поиска источников, ~/.profileкогда вы помещаете свой общий сеанс). инициализацый).

Стефан Шазелас
источник
Это не проходит checkbashismsиз-за используемой переменной.
Томас Дики
2
@ThomasDickey, да, но это тот случай, когда отчет о контрольных проверках следует игнорировать. Все остальные решения, приведенные до сих пор, значительно хуже.
Стефан Шазелас
@ StéphaneChazelas Вы говорите, что все остальные решения хуже. Что было бы не так с использованием $0для этого конкретного случая?
Энтони Дж - правосудие для Моники
2
@AnthonyGeoghegan Это не делает различий между bash, называемым as, shи некоторой другой оболочкой, называемой as sh.
Жиль "ТАК - перестань быть злым"
Это также дает ложный положительный результат, если dashили с другой ошибкой вызвана не-bash-оболочка argv[0] == "bash", что вполне законно, если маловероятно.
Кевин
17

Обычно $0для этого используют. На сайте, на который вы ссылаетесь, стоит:

0
   (Zero.) Expands to the name of the shell or shell script.
jimmij
источник
Так просто! Я настолько привык к использованию $0имени сценария оболочки, что очистился, забыл, что он также может ссылаться на текущую оболочку. Благодарность!
Энтони Дж. - правосудие для Моники
1
Как обычно, это соглашение соблюдается всеми хорошо управляемыми программами, но вредоносная программа может связываться с вами, используя различные системные вызовы из execсемейства, потому что они позволяют вызывающей программе идентифицировать двоичный файл, который будет запускаться отдельно от строки, передаваемой в качестве аргумента. 0.
dmckee
Это работает на .profile :) мило
Роб
5

Обычно переменная окружения SHELL сообщает вам оболочку по умолчанию. Вам не нужно вручную исходить из файла .bashrc (не так, смотрите обновление ниже), bash должен делать это автоматически, просто убедитесь, что он находится в каталоге $ HOME.

Другой вариант - сделать что-то вроде:

 ps -o cmd= $$

это скажет вам команду (без аргументов, = без значения не будет отображать заголовки столбцов) текущего процесса. Пример вывода:

 $ps -o cmd= $$
 bash
 $sh
 $ps -o cmd= $$
 sh

ОБНОВИТЬ:

Я исправлюсь! :)

Файл .bashrc не всегда получен, как упоминалось в комментариях ниже и /programming/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

Таким образом, вы можете переместить ваш .bashrc в .bash_profile и посмотреть, работает ли он без необходимости делать тест. Если нет, у вас есть тест выше.

обкрадывать
источник
1
.bashrcна самом деле не получены из оболочки входа в систему, но .profile(если она существует) или .bash_profileесть. SHELLне указывается в POSIX и, к сожалению, не является параметром cmdформата для ps.
Энтони Дж - правосудие для Моники
1
Это то, что я бы использовал, но это также подвергается злонамеренным манипуляциям.
dmckee
Обратите внимание, что хотя метод ps -o cmd= $$должен работать практически везде, $SHELLпеременная не имеет значения. Это оболочка пользователя по умолчанию, которая не имеет отношения к тому, какая оболочка запущена в данный момент или какая оболочка выполняет сценарий оболочки.
Terdon
2
Не на самом деле нет. Многие оболочки читаются ~/.profileпри запуске в качестве оболочек для входа. Так, например, твой $SHELLмог быть cshи ты бежишь bash -l. Это запустит bash как оболочку входа в систему, поэтому он будет читать ~/.profile, но вы $SHELLвсе равно будете указывать csh. Кроме того, .profileможет быть получен явно другим сценарием. Так как OP идет на максимальную надежность, следует рассмотреть все виды случаев. В любом случае $SHELLне предоставляет никакой полезной информации о запущенной в данный момент оболочке.
Terdon
2
FWIW, я тоже исправляюсь - о SHELLтом, что POSIX не указал: pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Энтони Дж - правосудие для Моники
4

В вопросе задается оболочка для входа в систему пользователя, а также текущая оболочка checkbashisms. Если предполагается, что это оболочка, в которой пользователь вошел в систему, я бы использовал одну из /etc/passwd, например:

MY_UID=$(id -u)
MYSHELL=$(awk -F: '$3 == '$MY_UID'{ print $7; }' </etc/passwd )

Пользователи могут, конечно, запустить новую оболочку после входа в систему. Конечно, если одна из них bash, а другая нет, тесты переменных среды bash могут не помочь.

Некоторые могут захотеть использовать getentне просто passwdфайл (но это не входит в сферу вопроса).

На основании комментария о LDAP и предложения для lognameэтой альтернативной формы можно использовать:

MY_NAME=$(logname)
MYSHELL=$(getent passwd | awk -F: '$1 ~ /^'$MY_NAME'$/ {print $7;}' )

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

Томас Дики
источник
1
Предполагая, что база данных пользователей находится в / etc / passwd (не LDAP / NIS / mysql ...), и что для идентификатора пользователя есть только одно имя пользователя. (было бы лучше проверить первый столбец против $(logname)).
Стефан Шазелас
iirc, POSIX не определяет необходимую утилиту (или я бы использовал ее в своем ответе). Использовать idли переменную, изменяемую пользователем, - другой выбор.
Томас Дики
1
Обратите внимание, что я сказал $(logname), нет $LOGNAME. Идентификатор пользователя и оболочка входа в систему происходят от имени пользователя, используемого при входе в систему. Вы не можете вернуться от идентификатора пользователя к оболочке входа в систему, кроме как в системах, где для каждого идентификатора пользователя есть только одно имя пользователя. Или, IOW, первичный ключ в базе данных пользователей - это имя пользователя, а не uid.
Стефан Шазелас
Спасибо @ThomasDickey за ответ с другой точки зрения. Однако это немного сложнее, чем я думал (больше похоже на ответы Джимми и Стефана ). Одна из причин, по которой я настраиваю свои файлы конфигурации оболочки под контроль версий, заключается в том, чтобы настроить свою среду на всех системах, в которых я в данный момент регулярно выполняю вход: Cygwin, Ubuntu и CentOS (обе версии 5 и 7, с использованием Active Directory для пользователя. аутентификация). По этой причине я предпочитаю сохранять логику максимально простой.
Энтони Дж - правосудие для Моники