Как я могу предотвратить появление неподдерживаемых опций 'shopt' в моем .bashrc?

9

Я работаю в относительно гетерогенной среде, где я могу использовать разные версии Bash на разных узлах HPC, виртуальных машинах или моей личной рабочей станции. Поскольку я помещаю свои сценарии входа в Git-репозиторий, я хотел бы использовать один и тот же (ish) .bashrcпо всем направлениям, без большого количества «если этот хост, то ...» - типа беспорядка.

Я как поведение по умолчанию Bash ≤ 4.1, расширяющегося cd $SOMEPATHв cd /the/actual/pathпри нажатии на Tabклавишу. В Bash 4.2 и выше вам нужно было shopt -s direxpandбы повторно включить это поведение, которое не было доступно до 4.2.29 . Это всего лишь один пример; другая, возможно связанная с этим shoptопция complete_fullquote(хотя я точно не знаю , что она делает) также могла изменить поведение по умолчанию в v4.2.

Тем direxpandне менее, это не распознается в более ранних версиях Bash, и, если я попытаюсь сделать это shopt -s direxpandв своей .bashrc, это приведет к тому , что сообщение об ошибке будет выводиться на консоль каждый раз, когда я вхожу в узел с более старой версией Bash:

-bash: shopt: direxpand: invalid shell option name

Что я хотел бы сделать, так это обернуть условное окружение, shop -s direxpandчтобы активным образом включить эту опцию в Bash> 4.1, не раздражая старые версии Bash ( т.е. не просто перенаправляя вывод ошибок в /dev/null).

Чувак пребывает
источник
Как мой ответ не помог?
Лучано Андресс Мартини
@LucianoAndressMartini Это так и есть, и это решение, которое я решил использовать самостоятельно .bashrc. Я все еще хотел записать, как использовать $BASH_VERSINFOдля опроса основную / младшую версию работающей оболочки, для моего собственного назидания, поэтому я закончил публиковать свой собственный ответ. :)
TheDudeAbides
Посмотрите в моем ответе, у меня есть кое-что о сравнении версии программы со сценарием оболочки.
Лучано Андресс Мартини

Ответы:

14

Проверьте, direxpandприсутствует ли в выходных данных shoptи включите его, если это:

shopt | grep -q '^direxpand\b' && shopt -s direxpand
Лучано Андресс Мартини
источник
4
Лучше сделать это grep -q '^direxpand\b'в случае, если у какой-либо будущей версии или ветки bash есть опция, которая содержит это как подстроку и удаляет direxpand. Маловероятно в этом конкретном случае, но это не стоит много, чтобы быть надежным.
Жиль "ТАК - перестань быть злым"
Спасибо Лучано. Я намеревался ответить на свой вопрос, но я приму ваш ответ после того, как мои изменения пройдут экспертную оценку. Может быть, вы сможете утвердить их самостоятельно?
TheDudeAbides
4
Bash позволяет запрашивать конкретные параметры оболочки, так что можно использовать [ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand. Больше никаких проблем с регулярными выражениями! :-)
Дэвид Фёрстер
@DavidFoerster Я бы перевернул логику: [ -n "blah" ] && shopt blahкак бы вы это ни сформулировали, вы говорите: «Если direxpand не поддерживается, то не делайте этого».
Богатый
1
@Rich: Большинство моих сценариев оболочки находятся set -eв верхней части, поэтому я склонен использовать сокращенную логику для этого.
Дэвид Фёрстер
16

Я не вижу, что не так с перенаправлением ошибок на /dev/null. Если вы хотите, чтобы ваш код был устойчивым set -e, используйте общую идиому … || true:

shopt -s direxpand 2>/dev/null || true

Если вы хотите запустить некоторый запасной код, если опция не существует, используйте статус возврата shopt:

if shopt -s direxpand 2>/dev/null; then
   # the direxpand option exists
else
   # the direxpand option does not exist
fi

Но если вам действительно не нравится перенаправление ошибки, вы можете использовать механизм завершения, чтобы выполнить самоанализ. Это предполагает, что у вас нет устаревших машин с bash ≤ 2.03, которые не имели программируемого завершения.

shopt_exists () {
  compgen -A shopt -X \!"$1" "$1" >/dev/null
}
if shopt_exists direxpand; then
  shopt -s direxpand
fi

Этот метод позволяет избежать разветвления, что медленно в некоторых средах, таких как Cygwin. То же самое 2>/dev/nullможно сказать и о простом , я не думаю, что вы можете победить это по производительности.

Жиль "ТАК - прекрати быть злым"
источник
Это не то место, куда ушел бы мой мозг, но мне нравится это compgenпредложение. Это вещи уровня университета прямо здесь! Избегать перенаправления на /dev/nullэто просто личное предпочтение. Я хотел бы попросить разрешения вместо прощения, если это имеет смысл? :)
TheDudeAbides
+1 за совершенно неожиданное школьное обучение в программируемом окончании Bash, что заставило меня перейти к руководству, чтобы расшифровать то, что compgen -A shopt -X ...даже значило.
TheDudeAbides
4
@TheDudeAbides Я читал об использовании compgenэтого способа в Unix и Linux , я не знаю, кто первым предложил это. (Я прекратил использовать bash в качестве моей основной оболочки до того, как она завершилась программируемым завершением.) В программировании обычно плохая идея запрашивать разрешение, потому что есть риск, что проверка разрешения не будет соответствовать тому, что вы на самом деле делаете, либо из-за кодирования ошибка (когда вы не совсем проверяете то, что, по вашему мнению, проверяете) или потому что то, что вы проверяли, изменилось до того, как вы его использовали .
Жиль "ТАК - перестань быть злым"
5

Когда вы точно знаете, что определенная shoptопция доступна в определенной основной / второстепенной версии патча Bash, вы можете проверить $BASH_VERSIONпеременную или элементы $BASH_VERSINFO[]массива, чтобы включить ее условно.

Вот тест для Bash 4.2.29 или более поздней версии, где direxpand впервые была представлена серия 4.2:

if [[ $BASH_VERSION == 4.2.* && ${BASH_VERSINFO[2]} -ge 29 ]] ||
   [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 3 ]] ||
   [[ ${BASH_VERSINFO[0]} -ge 5 ]]; then
    shopt -s direxpand
fi

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

Обратите внимание на скобки вокруг , которые будут необходимы, а также использование и , которые делают целое число , а не (локалите-зависимые) лексические сравнений. Если не заключено в кавычки , RHS оператора рассматривается как шаблоны "extglob" в Bash / условных выражениях , как отмечено здесь , что делает более эстетичным сравнение "начинается с", чем регулярное выражение, IMO.${BASH_VERSINFO[index]}-eq-gt==[[]]

$BASH_VERSINFOМассив содержит всю информацию , которую вы хотите видеть на выходе bash --version:

bash --version | head -1
# result:
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

declare -p BASH_VERSINFO
# result:
# declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'

Когда не ясно из документации , shoptв которой Bash версии (s) стали поддерживаться или изменить свое поведение, метод , предложенный Лучано отлично:

# note the '-q' so that the matched pattern isn't actually printed
shopt | grep -q direxpand && shopt -s direxpand

... как и решение, предложенное Жилем, просто игнорировать error ( shopt -s direxpand 2>/dev/null) и, возможно, проверять, $?если это абсолютно необходимо.

Список литературы: 1 , 2 , 3
Связанное чтение: Сета и Shopt - Почему два?

Чувак пребывает
источник
Вы также можете использовать что-то вроде if [[ $BASH_VERSION > 4.3 ]];(что соответствует 4.3.0и 5.0т. Д., Но также 4.3.0-alpha. Я не знаю, имеет ли значение более поздний факт.)
ilkkachu
Привет @ilkkachu. Спасибо за ваши изменения для покрытия Bash v5.x. direxpandВариант действительно доступен для Bash 4.2, хотя; Я проверил это с Докер изображением на v4.2.53, запустив docker run --rm bash:4.2 bash -c shopt | grep direxpand(и, для хорошей меры, что это действительно не доступно на v4.1.17, запустив docker run --rm bash:4.1 bash -c shopt | grep direxpand).
TheDudeAbides
ну хорошо, я проверил 4.2.0и наткнулся на тот факт, что это не сработало там. В списке изменений также упоминается, что он добавлен в bash-4.3-alpha. Тогда я полагаю, что нужно будет ${BASH_VERSINFO[2]}быть точным, но я не знаю, в какой момент релиз добавил это ...
ilkkachu
Я думаю, что мы в основном доказали точку зрения Жиля, изложенную выше; что на самом деле лучше просто попытаться включить опцию оболочки, а затем разобраться с ошибкой (или подавить ее), если она не поддерживается.
TheDudeAbides