В Bash, когда для псевдонима, когда для сценария, и когда написать функцию?

360

У меня ушло почти 10 лет использования Linux, чтобы задать этот вопрос. Все это было методом проб и ошибок и случайным ночным интернет-серфингом.

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

Что касается псевдонимов, я использую псевдонимы для очень простых операций, которые не принимают аргументов.

alias houston='cd /home/username/.scripts/'

Это кажется очевидным. Но некоторые люди делают это:

alias command="bash bashscriptname"

(и добавьте его в .bashrcфайл)

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

Потому что именно здесь я просто поместил бы что-нибудь в мою PATH и chmod +xэто, и это еще одна вещь, которая пришла после многих лет проб и ошибок в Linux.

Что подводит меня к следующей теме. Например, я добавил скрытую folder ( .scripts/) в домашнем каталоге в свой PATH, просто добавив строку в my .bashrc( PATH=$PATH:/home/username/.scripts/), чтобы все выполняемые там файлы автоматически выполнялись автоматически.

Если бы мне было нужно.

Мне правда это не нужно, правда? Я бы использовал это только для языков, которые не являются оболочкой, таких как Python.

Если это оболочка, я могу просто написать функцию в том же самом .bashrc:

funcname () {
  somecommand -someARGS "$@"
}

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

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

Но я что-то пропустил?

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

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

ixtmixilix
источник
5
возможный дубликат функций bash против скриптов
Gilles
10
+1 за указание всех подмножеств {alias, script, function}, на которые этот вопрос не направлен. +1 за детскую веру, что для вас было нормально опустить нулевое подмножество.
Томас Л. Холэдэй
1
Хотя вопрос, конкретно задаваемый о bash, следует учитывать, что у более старых оболочек Bourne были псевдонимы, но не функции. Это может иметь значение, если вы беспокоитесь о совместимости.
AndyB
Если .bashrc действительно лучшее место или хотя бы солидное место, для этого? Есть много способов сделать то же самое в Linux, и я ценю это, однако, при прочих равных, я предпочитаю делать вещи наиболее распространенным способом.
Kit10

Ответы:

242

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

# Make ls output in color by default.
alias ls="ls --color=auto"
# make mv ask before overwriting a file by default
alias mv="mv -i"

Функция должна использоваться, когда вам нужно сделать что-то более сложное, чем псевдоним, но это не будет полезно само по себе. Например, возьмите этот ответ на вопрос, который я задал, об изменении grepповедения по умолчанию в зависимости от того, находится ли оно в конвейере:

grep() { 
    if [[ -t 1 ]]; then 
        command grep -n "$@"
    else 
        command grep "$@"
    fi
}

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

Если вы получаете слишком много функций или функций слишком большого размера, поместите их в отдельные файлы в скрытом каталоге и отправьте в ваш ~/.bashrc:

if [ -d ~/.bash_functions ]; then
    for file in ~/.bash_functions/*; do
        . "$file"
    done
fi

Скрипт должен стоять сам по себе. Он должен иметь значение как нечто, что можно использовать повторно или использовать для более чем одной цели.

Kevin
источник
14
Также важно помнить, что - если он не снабжен .или source- скрипт выполняется отдельным процессом bash и имеет свою собственную среду. По этой причине все, что изменяет среду оболочки (например, функции, переменные и т. Д.), Не сохраняется в среде оболочки, из которой вы запускаете скрипт.
Будет ли Vousden
260

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

Псевдонимы и функции ¹

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

Сценарии

  • Оболочки не хранят скрипты в памяти. Вместо этого сценарии считываются из файлов, в которых они хранятся, каждый раз, когда они необходимы. Если сценарий найден с помощью $PATHпоиска, многие оболочки хранят в памяти хэш своего имени пути, чтобы сэкономить время при последующих поисках, $PATHно это означает, что объем памяти, занимаемой сценарием, не используется.
  • Скрипты могут вызываться разными способами, чем функции и псевдонимы. Они могут быть переданы в качестве аргумента интерпретатору, например sh script, или вызваны непосредственно как исполняемый файл, и в этом случае интерпретатор в строке shebang (например #!/bin/sh) вызывается для его запуска. В обоих случаях сценарий запускается отдельным процессом интерпретатора со своей собственной средой, отдельной от среды вашей оболочки, на среду которой скрипт никак не может повлиять. Действительно, оболочка интерпретатора даже не должна соответствовать вызывающей оболочке. Поскольку сценарии, вызываемые таким образом, ведут себя как любой обычный исполняемый файл, они могут использоваться любой программой.

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

заявка

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

  • Нужно ли другим программам помимо вашей оболочки использовать его? Если так, то это должен быть скрипт.

  • Вы хотите, чтобы он был доступен только из интерактивной оболочки? Обычно требуется изменить поведение многих команд по умолчанию при интерактивном запуске, не затрагивая внешние команды / сценарии. В этом случае используйте псевдоним / функцию, установленный в rc-файле оболочки «only-mode-only» (для bashэтого есть .bashrc).

  • Нужно ли менять среду оболочки? И функция / псевдоним или исходный скрипт являются возможными вариантами.

  • Это то, что вы часто используете? Вероятно, более эффективно хранить его в памяти, поэтому, если возможно, сделайте его функцией / псевдонимом.

  • И наоборот, это то, что вы используете редко? В этом случае нет смысла использовать память, когда она вам не нужна, поэтому сделайте это сценарием.


Functions Хотя у функций и псевдонимов есть некоторые важные различия, они сгруппированы вместе, потому что функции могут делать все, что могут псевдонимы. Псевдонимы не могут иметь локальных переменных и не могут обрабатывать аргументы, и они неудобны для чего-либо длиннее одной строки.

² Каждый запущенный процесс в системе Unix имеет среду, состоящую из variable=valueпары пар, которые часто содержат глобальные параметры конфигурации, например, LANGдля локали по умолчанию и PATHдля указания исполняемого пути поиска.

jw013
источник
26
ИМХО Это лучший ответ.
Люк М
4
Формат вопроса / ответа - отличная идея. Я мог бы украсть это. ;-)
Микель
4
Стоит отметить: если два (или более) скрипта должны совместно использовать какой-либо код, вероятно, лучше всего поместить этот код в функцию, которая сама находится в третьем файле, который оба этих скрипта импортируют / исходят.
Кболино
3
Еще один пункт, который нужно добавить в этот список вопросов: Вам когда-нибудь нужно менять функциональность команды на лету? Изменения в сценарии будут отражены во всех сеансах, тогда как функции и псевдонимы должны быть перезагружены или переопределены для каждого сеанса.
Stratus3D
3
Хороший ответ. Еще одна важная вещь (для меня): при создании «ярлыка» для других утилит лучше использовать псевдоним, потому что существующее автозаполнение будет работать только с псевдонимами, но не со скриптами или функциями (так что +1 для псевдонимов). Например, создавая alias g='gradle'автодополнение Gradle при использовании своего gпсевдонима, но не получая его из коробки при использовании сценария gradle $*или функции сgradle $@
Yoav Aharoni
37

Я думаю, что это на вкус каждого человека. Для меня логика выглядит так:

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

Там действительно нет ничего, чтобы ограничить вас от того, что делает что-то, что работает .

phunehehe
источник
6
Я часто пропускаю опцию функции и сразу пишу сценарий. Но я согласен, что это отчасти дело вкуса
Бернхард
2
Функция начинает иметь смысл, если вам это нужно в нескольких скриптах.
Нильс
7
... или если вам нужны побочные эффекты для изменения текущей оболочки.
Гленн Джекман
имеет смысл, чувак!
исследователь
15

По крайней мере, частично это вопрос личного вкуса. С другой стороны, есть некоторые четкие функциональные различия:

  • псевдонимы: подходит только для простых замен текста, без аргументов / параметров
  • функции: легко написать / использовать, полная возможность написания сценариев оболочки, доступна только внутри bash
  • скрипты: более или менее похожи на функции, но доступны (могут быть вызваны) и вне bash

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

PS: alias command="bash bashscriptname"я не вижу никакой причины делать это. Даже если bashscriptnameне в $ PATH, простого alias c=/path/to/scriptбудет достаточно.

nohillside
источник
1
В alias command="bash bashscriptname"скрипте не обязательно должен быть исполняемый файл; в alias c=/path/to/scriptэто должно.
Мартин - ン ー チ ン
Это совсем не так, что функции «доступны только внутри Bash». Если вы хотите сказать, что они предназначены только для Bash, то это просто ложь (оболочка Bourne и все совместимые производные имеют их); и если вы хотите сказать, что они являются функцией интерактивных оболочек, это тоже не совсем верно (хотя псевдонимы, переменные и функции, определенные в файле, который загружается при запуске интерактивными оболочками, очевидно, не будут загружаться неинтерактивными оболочками).
tripleee
@tripleee Значение было больше похоже на «вы не можете выполнять exec()функции оболочки» :-)
nohillside
11

Вот некоторые дополнительные пункты об алиасах и функциях:

  • Одноименный псевдоним и функция могут сосуществовать
  • пространство имен псевдонимов ищется первым (см. первый пример)
  • псевдонимы не могут быть (не) установлены в подоболочках или неинтерактивной среде (см. второй пример)

Например:

alias f='echo Alias'; f             # prints "Alias"
function f { echo 'Function'; }; f  # prints "Alias"
unalias f; f                        # prints "Function"

Как мы видим, существуют отдельные пространства имен для псевдонимов и функций; Более подробную информацию можно найти с помощью declare -A -p BASH_ALIASESи declare -f f, которая печатает их определения (оба хранятся в памяти).

Пример, показывающий ограничения псевдонимов:

alias a='echo Alias'
a        # OK: prints "Alias"
eval a;  # OK: prints "Alias"
( alias a="Nested"; a );  # prints "Alias" (not "Nested")
( unalias a; a );         # prints "Alias"
bash -c "alias aa='Another Alias'; aa"  # ERROR: bash: aa: command not found

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

Наконец, обратите внимание, что вы можете иметь произвольные вычисления в псевдониме, объявив функцию a, немедленно вызывающую ее, например:

alias a_complex_thing='f() { do_stuff_in_function; } f'

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

leden
источник
10

Когда писать сценарий ...

  • Скрипты собирают программные компоненты (иначе как инструменты, команды, процессы, исполняемые файлы, программы) в более сложные компоненты, которые сами могут быть собраны в еще более сложные компоненты.
  • Скрипты обычно делаются исполняемыми, поэтому их можно вызывать по имени. При вызове создается новый подпроцесс для запуска сценария. Копии любых exportпеременных ed и / или функций передаются по значению в сценарий. Изменения в этих переменных не распространяются обратно на родительский скрипт.
  • Скрипты также могут быть загружены (получены), как если бы они были частью вызывающего скрипта. Это аналогично тому, что некоторые другие языки называют «импорт» или «включить». При получении они выполняются в рамках существующего процесса. Подпроцесс не создается.

Когда написать функцию ...

  • Функции - это предварительно загруженные скрипты оболочки. Они работают немного лучше, чем вызов отдельного скрипта, но только если он должен быть прочитан с механического диска. Сегодняшнее распространение флэш-накопителей, SSD и обычное кэширование Linux в неиспользуемой оперативной памяти делают это улучшение в значительной степени неизмеримым.
  • Функции служат основным средством bash для достижения модульности, инкапсуляции и повторного использования. Они улучшают наглядность, надежность и удобство обслуживания сценариев.
  • Синтаксические правила для вызова функции идентичны правилам вызова исполняемого файла. Функция с тем же именем, что и исполняемый файл, будет вызываться вместо исполняемого файла.
  • Функции являются локальными для сценария, в котором они находятся.
  • Функции могут быть экспортированы ( скопированы по значению ), поэтому они могут использоваться внутри так называемых скриптов. Таким образом, функции распространяются только на дочерние процессы, а не на родителей.
  • Функции создают многократно используемые команды, которые часто собираются в библиотеки (сценарий только с определениями функций) для получения из других сценариев.

Когда писать псевдоним ...

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

# A bash in-script 'alias'
function oldFunction () { newFunction "$@"; }
DocSalvager
источник
9

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

Это может быть важно для производительности - функция быстрее, потому что это не так fork()и exec(). В обычных обстоятельствах это различие тривиально, но если вы отлаживаете систему, в которой недостаточно памяти и которая перебирает страницы, это может иметь большое значение.

Кроме того, если вы хотите изменить текущую среду оболочки, вы должны использовать функцию. Например, функция может изменить поиск команд $PATHдля текущей оболочки, а сценарий - нет, поскольку работает с копией fork / exec $PATH.

Ян Стейнман
источник
Как это распространение функций для детей работает?
HappyFace
1
@HappyFace В Bash вы можете export -fиспользовать функцию, хотя точная внутренняя работа этого не совсем ясна. Я считаю, что это не переносимо для традиционной оболочки Bourne.
tripleee
7

Скрипт и псевдоним, а также скрипт и функция не являются взаимоисключающими. Вы можете хранить псевдонимы и функции в скриптах.

Скрипты - это просто код, который сделан постоянным . Полезные функции и псевдонимы, которые вы хотели бы использовать в будущем, хранятся в скриптах. Однако сценарий часто представляет собой набор из более чем одной функции.

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

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

неизвестный пользователь
источник
5

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

Если он должен использоваться вне вашей предпочитаемой оболочки, сделайте его сценарием. 1

Если он принимает аргументы, сделайте это функцией или скриптом.

Если он должен содержать специальные символы, сделайте его псевдонимом или скриптом. 2

Если ему нужно работать с sudo, сделайте его псевдонимом или скриптом. 3

Если вы хотите легко изменить его, не выходя из системы и не входя в нее, скрипт станет проще. 4

Сноски

1 Или сделайте его псевдонимом, вставьте ~/.envи установите export ENV="$HOME/.env", но сложно заставить его работать переносимо.

2 Имена функций должны быть идентификаторами, поэтому они должны начинаться с буквы и могут содержать только буквы, цифры и символы подчеркивания. Например, у меня есть псевдоним alias +='pushd +1'. Это не может быть функцией.

3 И добавьте псевдоним alias sudo='sudo '. То же самое относится и к любой другой команде, такой как strace, gdbи т. Д., Которая принимает команду в качестве первого аргумента.

4 Смотрите также: fpath. Конечно, вы также можете сделать source ~/.bashrcили подобное, но это часто имеет другие побочные эффекты.

Mikel
источник
1
Я не знал, что ты можешь использовать псевдоним +в bash. Интересно, что после тестирования я обнаружил, что в bash вы можете создать +псевдоним, но не функцию, как вы говорите, но zsh - наоборот - +может быть функцией, но не псевдонимом.
Кевин
В zshвас есть писать alias -- +='some command here'.
Микель
Почему-то я не думаю, что псевдонимы +переносимы. См.
Спецификацию
3
Upvote для покрытия sudoиспользования. Что касается сноски 4, я храню свои псевдонимы ~/.bash_aliasesи определения функций, ~/.bash_functionsчтобы я мог легко sourceих перечитать (без опасности побочных эффектов).
Энтони Геогеган
3

Просто чтобы добавить несколько заметок:

  • С sudo можно использовать только отдельный скрипт (например, если вам нужно отредактировать системный файл), например:
sudo v /etc/rc.conf  #where v runs vim in a new terminal window;
  • Только псевдонимы или функции могут заменить системные команды под тем же именем (при условии, что вы добавляете dir сценариев в конец PATH, что я считаю целесообразным для обеспечения безопасности в случае случайного или злонамеренного создания сценария с именем, идентичным системной команде), например:
alias ls='ls --color=auto'  #enable colored output;
  • Псевдонимы и функции требуют меньше памяти и времени для выполнения, но требуют времени для загрузки (поскольку оболочка должна интерпретировать их все, прежде чем показывать вам приглашение). Учитывайте это, если вы регулярно запускаете новые процессы оболочки, например:
# pressing key to open new terminal
# waiting for a few seconds before shell prompt finally appears.

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

Корвин
источник
5
Псевдонимы также могут быть использованы с sudo. Но сначала тебе нужно alias sudo='sudo '.
Микель
Хотя верно то, что выполнение скрипта на мгновение потребляет больше памяти на время выполнения fork + exec, при загрузке большого количества кода в память текущего экземпляра оболочки он потребляет больше памяти в будущем, часто для хранения кода, который только привыкает довольно редко.
tripleee
2

Мое эмпирическое правило:

  • псевдонимы - одна команда, без параметров
  • функции - одна команда несколько параметров
  • скрипт - несколько команд, без параметров
Майкл Даррант
источник
1

В многопользовательской (или многопользовательской) среде я использую сценарии для всего, даже если это просто короткая обертка «exec what ....».

Конечно, это технически медленнее / менее эффективно, чем псевдоним или функция, но это почти никогда не имеет значения - и при условии, что он находится в пути, скрипт всегда работает.

Ваша функция может быть вызвана из cron, из чего-то с ограниченной или измененной средой, такой как sudo или env, или пользователь может просто использовать другую оболочку для вас - все или которая может нарушить псевдоним или функцию.

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

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

T

tjb63
источник
0

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

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

У меня есть сценарий с ~/.bin/именем , setupкоторый выполняет следующие действия : 1

  1. приводит меня в определенный каталог.
  2. определяет несколько переменных.
  3. печатает сообщения о состоянии каталога. 2

Дело в том, что если бы я просто запустил, у setup <project-name>меня не было бы этих переменных, и я бы вообще не попал в каталог. Лучшим решением, которое я нашел, было добавление этого сценария PATHи добавление alias setup=". ~/.bin/setup"к нему ~/.bashrcили что-то еще.

Примечания:

  1. Для этой задачи я использовал скрипт, а не функцию не потому, что он очень длинный, а потому, что я могу его редактировать, и мне не нужно получать исходный файл после редактирования, если я хочу обновить его использование.
  2. Подобный случай произошел со мной, когда я создал скрипт для перезагрузки всех моих файлов точек. 3
  1. Сценарий доступен в моем репозитории dotfiles в разделе .bin/.
  2. О скрипте : я даю этому скрипту аргумент, который является именем для проекта, который я определил в расширенном. После этого скрипт знает, чтобы привести меня в нужную директорию в соответствии с определенным csvфайлом. Переменные, которые он определяет, взяты из make-файла в этом каталоге. Сценарий запускается позже ls -lи git statusпоказывает мне, что там происходит.
  3. Этот скрипт также доступен в моем репозитории dotfiles .bin/.
Дорон Бехар
источник
1
Хм, кажется, это просто функция, а не комбинация псевдоним-скрипт. (Кстати, среда пишется как «среда», а не «среда».)
Wildcard
Спасибо за комментарий по поводу опечатки, я исправлю это на следующем коммите. Что касается использования функции вместо скрипта - возможно, я буду использовать функцию для этой конкретной задачи и отброшу эти псевдонимы. Дело в том, что иногда довольно просто использовать скрипт и псевдоним, если вы время от времени редактируете этот скрипт .
Дорон Бехар
0

Когда писать сценарий

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

Это включает в себя vim (для меня): написав фильтры и другие программы в виде скриптов, я могу сделать что-то вроде :%!my-filterфильтрации файла через программу из моего редактора.

Если бы my-filterэто была функция или псевдоним, это было бы невозможно.

Д. Бен Кнобл
источник