Зачем мне создавать псевдоним, который создает функцию?

Я вижу этот шаблон время от времени, особенно в вопросах настройки Bash.

alias f='_ () { useful code; }; _'

Я вообще не вижу причин создавать здесь псевдоним. Очевидный рефакторинг

f () { useful code; }

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

Например, Создать псевдоним Bash, который принимает параметр? имеет несколько ответов, которые демонстрируют эту технику. bash-скрипт для запуска lftp с путем ftp - это вопрос, в котором есть код, подобный этому, в вопросе о реальной функциональности внутри функции, и ОП не объясняет почему, даже если я осторожно подтолкнул.

Это просто антипаттерн или есть реальная причина сделать это? При каких обстоятельствах этот дизайн имеет смысл?

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

Ответ 1

Я нашел это https://unix.stackexchange.com/questions/30925/in-bash-when-to-alias-when-to-script-and-when-to-write-a-function/233751#233751, которое объясняет преимущества определения функции в псевдониме.

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

Ответ 2

Вот мои 2 цента на это, и это представляет мое личное мнение, а также понимание по теме.

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

Пример:

alias kgps='kubectl get pods --all-namespaces | grep '

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

alias kdp="_(){ kubectl get pods --all-namespaces  | grep \$1 | awk '{print \$2}' | xargs kubectl delete pod; }; _"

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

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

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

Псевдонимы могут более легко переопределять системные команды по сравнению с функциями

Если мне нужно переопределить ls, я могу сделать это намного проще с alias

alias ls='ls -altrh'

Хотя функция, эквивалентная той же самой, будет выглядеть так, как показано ниже

ls() { command ls -altrh "[email protected]";}
ls() { /bin/ls -altrh "[email protected]";}

Псевдонимы предназначены в основном для ярлыков

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

Псевдонимами легче управлять

Запустите команду alias, чтобы получить список активных в настоящий момент псевдонимов

$ alias
....
vs='vagrant ssh'
vu='vagrant up'
vus='vu && vs'
....

Чтобы получить список функций, нам нужно использовать declare -f или другую аналогичную команду

$ declare -f | wc -l
  8226
$ alias | wc -l
  217

Теперь, если я публикую частичный вывод declare -f, я получу

$ declare -f
...
vi_mode_prompt_info () {
    return 1
}
virtualenv_prompt_info () {
    return 1
}
work_in_progress () {
    if $(git log -n 1 2>/dev/null | grep -q -c "\-\-wip\-\-")
    then
        echo "WIP!!"
    fi
}
zle-line-finish () {
    echoti rmkx
}
zle-line-init () {
    echoti smkx
}
zsh_stats () {
    fc -l 1 | awk '{CMD[$2]++;count++;}END { for (a in CMD)print CMD[a] " " CMD[a]/count*100 "% " a;}' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl | head -n20
}

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

Синтаксис экранирования псевдонимов и функций отличается для системных команд

Чтобы избежать определенного псевдонима, вам необходимо добавить префикс \, в то время как для functions вам нужно использовать command <originalcommand> или абсолютный путь к команде /bin/originalcommand

Псевдонимы имеют более высокий приоритет над функцией

Посмотрите на приведенный ниже пример

alias ls='echo alias && ls'
$ ls() { /bin/ls -al }
alias
$ ls
alias
total 23173440
drwxrwxr-x+ 255 tarunlalwani  staff        8160 Jul 30 22:39 .
drwxr-xr-x+ 113 tarunlalwani  staff        3616 Jul 30 23:12 ..
...

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

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

Ответ 3

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

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

Ответ 4

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

alias f='_ () { echo "useful code"; }; _'
alias g='_ () { echo "Other useful code"; }; _'
alias h='_ () { echo "Special code"; }; _'

И теперь вы можете позвонить

f
_
g
_
h
_
f

@DavidC.Rankin правильно прокомментировал, что это выглядело ужасно.
Я согласен.
Я думал о том, как его использовать. Вы можете использовать его для тестирования программного обеспечения, что-то вроде

alias ok='commitTransaction () { echo "commited"; return 0; }'
alias nok='commitTransaction () { echo "unknown error"; return 1; }'
alias locked='commitTransaction () { echo "locked"; return 2; }'
alias slow='commitTransaction () { sleep 20; echo "commited"; return 0;  }'

И теперь тестер может запускать свои тестовые сценарии:

ok
# And now start ok test
nok
# And now start nok test

Все еще взламывая, почему бы не сделать лучшую пробирку?

Ответ 5

  Это просто антипаттерн (sic)...

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

alias foo='_() { echo $2 $3 $1; }; _'

И вот, это работает. Пользователи счастливы, и они идут дальше.

Но поскольку последовательность _() очень похожа на заклинание оболочки (2>&1, >> и т.д.), Пользователи никогда не думают, что _() является просто компактным синтаксисом для function _, и никогда не переходят к следующему шагу обучающие функции. С этим шаблоном псевдонимов они получают все преимущества функций и не должны изучать "новый" синтаксис. Скорее всего, никогда даже не заметят неприятного побочного эффекта: перезаписывают все предыдущие функции с именем _.

Я искал Usenet с 1981 по 1991, но я не нашел прямых доказательств этой теории.

... или есть реальная причина сделать это? При каких обстоятельствах этот дизайн имеет смысл?

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