Bash 带关键字和不带关键字的shell函数定义

Bash 带关键字和不带关键字的shell函数定义,bash,shell,zsh,Bash,Shell,Zsh,我花了很长时间才发现以下shell脚本无法工作的原因: if command -v z > /dev/null 2>&1; then unalias z 2> /dev/null z() { [ $# -gt 0 ] && _z "$*" && return cd "$(_z -l 2>&1 | fzf --height 40% --n

我花了很长时间才发现以下shell脚本无法工作的原因:

if command -v z > /dev/null 2>&1; then
    unalias z 2> /dev/null

    z() {
        [ $# -gt 0 ] && _z "$*" && return
            cd "$(_z -l 2>&1 |
                fzf --height 40% --nth 2.. --reverse --inline-info +s --tac \
                --query "${*##-* }" |
                sed 's/^[0-9,.]* *//')"
    }
fi
在本例中,函数定义需要函数关键字,
function z(){…}
。 没有它,我得到:

~/.shell/functions:112: defining function based on alias `z'
~/.shell/functions:112: parse error near `()'
我找不到任何地方表明在函数定义中使用或不使用
函数
关键字有任何区别。为什么这是本案的解决方案? (我在zsh和bash中尝试过)

手册页(manbash)声明“保留字函数是可选的”:

壳函数定义 shell函数是一个像简单命令一样调用的对象,它使用一组新的位置参数执行复合命令。Shell函数声明如下:

   name () compound-command [redirection]
   function name [()] compound-command [redirection]
          This  defines a function named name.  **The reserved word function is optional.**  If the function reserved word is supplied, the parentheses are optional.
发件人:

别名在读取命令时展开,而不是在执行命令时展开

因此,当读取
if
语句时,
z
将展开,而在执行语句时,不会展开。因此,即使您
unalias
,别名也已在
if
语句中展开(即
z()…
展开)

添加
函数
会有所帮助,因为别名仅在用作第一个单词时展开。如果在函数声明中添加
函数
,则不会展开任何内容


检查以下代码,该代码演示复合命令中别名的行为:

#!/usr/bin/env bash

shopt -s expand_aliases
alias greet='echo hello'

if true; then
    unalias greet 2> /dev/null

    #still outputs hello!
    greet  
    #not first word, outputs greet
    echo greet                                  
fi

#error!
greet
此代码段显示别名
foo
在执行之前确实得到了扩展。因此,声明了一个名为
bar
的函数,not
foo

$ alias foo='bar'
$ foo() { echo hello; }
$ declare -f foo
$ declare -f bar
bar () 
{ 
    echo hello
}

#declaring with 'function' keyword will work as expected
$ function foo { echo hi; }
$ declare -f foo 
foo () 
{ 
    echo hi
} 

进一步详细说明别名的行为,并建议执行以下操作:

为了安全起见,请始终将别名定义放在单独的行中,然后执行以下操作 不要在复合命令中使用别名


您为什么要将
z()
放在那个位置?我想覆盖z别名,以使用使用fzf“增强”它的函数。但是我想检查一下z在那之前是否已经安装好了。那不是
别名z=…
吗?抱歉,我的bash知识有点旧。顺便说一句,
function z(){
通常是最好避免的。存在
function
关键字是为了与旧的扩展ksh语法兼容,
function z{
(在使用此语法时,有几个行为变化,例如默认情况下使变量函数为本地;请注意,bash即使在使用
function
时也不会这样做),而parens格式是为了与POSIX sh兼容,
z(){
;当您将两者结合起来时,您会得到一些与旧ksh不兼容的东西,也与POSIX sh不兼容。有关更多讨论,请参阅。如果unalias z 2>/dev/null;则可以使用
,这样可以简化(但不会改变您的问题)。这正是我所指的。那么,为什么在这种情况下它不是可选的呢?哦,对不起,现在我明白你的意思了。它是可选的,因为你总是可以定义一个没有关键字的函数。该函数的名称可能取决于别名扩展。我想你混淆了函数和别名。它们是不同的。C要详细说明吗?我不同意。请再读一遍答案,告诉我它在哪些方面没有回答这个问题,在哪些方面混淆了函数和别名。讨论这两个问题是因为它们都与问题有关,仅此而已。@DeanHouseholder别名实际上与函数密切相关,因为函数是一个compound命令及其一起使用可能会导致不直观的行为。答案提供了此类行为的示例和解释。原始示例不包含任何别名。仅包含函数。但第2行试图使用
unalias
删除函数定义,这将产生错误。函数只能在y
unset
。您唯一需要使用关键字
function
来声明bash函数的时间是当您已经有一个使用相同名称定义的实际别名时。您的示例正确地演示了这一点。很抱歉否决您的投票。如果您编辑了您的答案,我将被允许向上投票。@DeanHouseholder没问题,只是想知道那么,我是否给出了一些错误的信息,是否应该编辑我的答案:)祝你过得愉快。