为什么我要创建一个创建函数的别名?

Why would I create an alias which creates a function?

我偶尔会看到这种模式,尤其是在有关 Bash 提示自定义的问题中。

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

我看不出有什么理由在这里创建别名。明显的重构

f () { useful code; }

它完全避免了声明别名,而只是一劳永逸地定义函数,看起来更简单、更容易理解、不那么脆弱,而且效率更高。 (以防万一,别名最终会在您每次调用别名时重新声明该函数。)

例如,Make a Bash alias that takes a parameter? has several answers which exhibit this technique. 是一个问题,在关于函数内部实际功能的问题中有这样的代码,即使我轻轻地刺激,OP 也没有解释为什么。

这只是一种反模式,还是有实际理由这样做?这种设计在什么情况下有意义?

不是 关于在别名后面带有 space 的别名,或者关于代码混淆(我发现的示例通常是完全可读的,除了这种神秘的技术).

我发现 an Ask Ubuntu question about a related topic 其中一个答案声称这是对不同设计原则的误解:给函数一个长的描述性名称,并为方便起见创建一个较短的别名。

这仍然无法解释为什么您每次都会让别名重新声明该函数。

您可以使用别名来打开和关闭您不想更改的功能。 假设您有调用函数 _ 的代码。您可以使用

切换另一个函数的实现
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

仍在黑客攻击,为什么不做一个更好的测试存根?

这是我对此的 2 美分,它代表我个人的意见以及对该主题的理解。

  • 对函数使用别名在某种程度上是开发人员的个人喜好。我将在这两种方法之间添加一些差异,这也可能考虑到使用别名与函数的个人偏好
  • 有时我想做的大部分事情都可以使用别名本身来实现,但只有少数需要使用参数。因此,我没有将别名与函数混合使用,而是将别名与函数本身一起使用

示例:

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

效果很好,我可以搜索我的 kubernetes pods。现在为了删除这些 pods,我需要在命令之间传递相同的参数,所以我在

中使用带有函数的别名
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 "$@";}
ls() { /bin/ls -altrh "$@";}

别名主要用于快捷方式

别名主要用于创建快捷命令,而函数用于很多事情,复杂的命令组合,自动完成,bash 提示

别名更易于管理

运行 alias 命令你得到当前活动别名的列表

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

要获取函数列表,我们需要使用 declare -f 或其他类似命令

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

现在如果我 post 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[]++;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 完成并提升问题中的格式

我发现这个 answer too [U&L] In Bash, when to alias, when to script, and when to write a function? 解释了在别名中定义函数的好处。

The benefit of doing so over declaring a function is that your alias cannot be simply overwritten by source-ing (or using .) a script which happens to declare a same-named function.

Is this just plainly an antipattern (sic)...

我认为它的流行程度可能只是cargo cult programming。别名很容易理解,因此用户通常会先了解它们。随着用户技能和需求的提高,他们发现别名缺乏灵活的参数处理。所以他们进行网络搜索,例如 "shell alias parameter passing",并找到建议模式的帖子:

alias foo='_() { echo   ; }; _'

瞧,它起作用了。用户很高兴,他们继续前进。

但由于_()序列看起来很像shell咒语(2>&1>>等),用户永远不会认为_() 只是 function _ 的紧凑语法,永远不会进入学习函数的下一步。使用这种别名模式,他们可以获得函数的所有好处,而不必学习 "new" 语法。很可能从来没有注意到令人讨厌的副作用:覆盖任何名为 _.

的先前函数

我搜索了 1981 年到 1991 年的 Usenet,但是我没有找到任何直接证据证明这一理论。

... or is there an actual reason to do this? Under what circumstances would this design make sense?

在我起草这个答案的五天里,我想到的每一个理由都回到了反对它的论点上。选择的答案 - 别名不能在 subshells 中被屏蔽 - 是一个充分的理由,虽然我从来没有想过是那个偏执狂:我不四处寻找源代码我没有完全审查。