Определение псевдонима в функциях bash

9

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

Вот минимальный пример:

# aliases.sh
alias fooAlias='echo "this will never work!"'  

,

# .bashrc
function setupLotsOfThings() {
    source aliases.sh
    fooAlias
}

,

Теперь, если я просто получу источник в aliases.shинтерактивном режиме, все будет работать так, как ожидается:

[mycomputer]~/ $ source aliases.sh
[mycomputer]~/ $ fooAlias
this will never work!

Однако, если я вместо этого вызову функцию, определенную в моем .bashrc, он не распознает псевдоним после определения его определения:

[mycomputer]~/ $ setupLotsOfThings
-bash: fooAlias: command not found

Что здесь происходит? Что-то мне не хватает в области действия aliasкоманды при использовании в функции?

Изменить: я добавлю некоторые детали помимо минимального примера, чтобы пролить свет на то, что я пытаюсь достичь.

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

Я хотел бы настроить свои собственные различные среды с помощью одной команды. В настоящее время я делаю что-то вроде:

[mycomputer]~/ $ source /some/environment/setup/script.sh
[mycomputer]~/ $ aliasToSetupSomeSoftwareVersion    #this was defined in the above
[mycomputer]~/ $ anotherAliasForOtherSoftware
[mycomputer]~/ $ source /maybe/theres/another/script.sh
[mycomputer]~/ $ runSomeOtherSetup      # this was defined in the new script

Эти команды обычно должны выполняться по порядку. Моя идея в основном заключалась в том, чтобы просто скопировать вышеупомянутые строки в функциональный блок, но, как показывает оригинальный пример, это просто не работает. Альтернативные обходные пути приветствуются!

гнаться
источник

Ответы:

10

Альтернативное решение - вставить эти команды в текстовый файл вместо функционального блока. Что-то вроде:

## This is needed to make the sourced aliases available
## within the script.
shopt -s expand_aliases

source /some/environment/setup/script.sh
aliasToSetupSomeSoftwareVersion
anotherAliasForOtherSoftware
source /maybe/theres/another/script.sh
runSomeOtherSetup

Сохраните это как setup1.shхотите. Хитрость заключается в том, чтобы затем источник этого файла, а не выполнить его:

$ source setup1.sh

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

Вы можете еще больше упростить процесс, добавив это в .bashrc:

alias setupLotsOfThings="source setup1.sh"

Теперь вы можете просто запустить setupLotsOfThingsи получить желаемое поведение из функции.


объяснение

Здесь есть два вопроса. Во-первых, псевдонимы недоступны для функции, в которой они объявлены, но только после выхода из этой функции, а во-вторых, псевдонимы недоступны в скриптах. Оба объяснены в том же разделе man bash:

Псевдонимы не раскрываются, когда оболочка не является интерактивной, если только параметр оболочки expand_aliases не установлен с помощью shopt (см. Описание shopt в разделе «Команды SHELL BUILTIN» ниже).

Правила, касающиеся определения и использования псевдонимов, несколько сбивают с толку. Bash всегда читает по крайней мере одну полную строку ввода перед выполнением любой из команд в этой строке. Псевдонимы раскрываются при чтении команды, а не при ее выполнении. Поэтому определение псевдонима, отображаемое в той же строке, что и другая команда, не вступает в силу до тех пор, пока не будет прочитана следующая строка ввода. На команды, следующие за определением псевдонима в этой строке, новый псевдоним не влияет. Такое поведение также является проблемой при выполнении функций. Псевдонимы раскрываются при чтении определения функции, а не при ее выполнении, поскольку определение функции само по себе является составной командой. Как следствие, псевдонимы, определенные в функции, будут
недоступны до тех пор, пока эта функция не будет выполнена.
Для безопасности всегда помещайте определения псевдонимов в отдельной строке и не используйте псевдоним в составных командах.

Тогда есть разница между выполнением и поиском файла. По сути, при запуске скрипта он запускается в отдельной оболочке, а источник - в текущей оболочке. Таким образом, sourcing setup.shделает псевдонимы доступными для родительской оболочки, в то время как выполнение скрипта не будет.

terdon
источник
Ну, я работаю над кластером, и у меня есть несколько таких сценариев "псевдонимов", которые помогают настраивать программные среды и т. Д. Сценарий псевдонимов определяет множество различных псевдонимов; Моя цель - вызвать определенную среду, используя правильные псевдонимы, а затем запустить (некоторые из них) эти псевдонимы. Я бы предпочел сделать это одной командой, чем выполнять несколько команд подряд.
Погоня за
И PS сценарий, который устанавливает псевдонимы, к сожалению, очень многословен и немного медленен (так как он касается многих файлов в NFS), поэтому я предпочитаю не получать все эти вещи, например, во время входа в систему.
Погоня
@ chase, но они получены только при запуске setupLotsOfThings, они просто не доступны самой функции. Они работают в оболочке, из которой вы вызвали функцию. В любом случае, если ваша функция использует только псевдонимы, почему бы просто не использовать псевдоним? Например: alias setupstuff="source aliases.sh".
тердон
верно, но я не беспокоюсь о масштабах как таковых . В идеале я просто хочу объединить «исходный материал» и «запустить псевдонимы» в один шаг. Возможно, это можно сделать с помощью 3 функций? function sourceStuff () {source ...}; function runStuff () {someAlias; ...}; function setupLotsOfThings () {sourceStuff; runStuff; };
Погоня
@ Да, я думал об этом, но это не сработало :). Не могли бы вы дополнить свой вопрос еще кое-чем из того, что вам нужно сделать? Функции могут иметь дело только с такой большой сложностью, что вам, возможно, придется написать небольшой скрипт.
тердон
7

На самом деле, ваши псевдонимы доступны после загрузки функции! Вы можете использовать их в своей интерактивной оболочке или в вашей .bashrcпосле выполнения функции.

Ограничение состоит в том, что псевдонимы в определении функции раскрываются при чтении определения функции, а не при оценке функции. Это ограничение bash. Так что это будет работать:

function setupLotsOfThings() {
    source aliases.sh
}
setupLotsOfThings
fooAlias

Но не это

function setupLotsOfThings() {
    source aliases.sh
}
function useTheAliases() {
    fooAlias
}
setupLotsOfThings
useTheAliases

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

Жиль "ТАК - перестань быть злым"
источник