Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shell function definition with and without keyword

Tags:

bash

shell

zsh

It took me forever to find out that the reason the following piece of shell script doesn't work:

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

is that the function definition, in this case, requires the function keyword, function z() {...}. Without it I get:

~/.shell/functions:112: defining function based on alias `z'
~/.shell/functions:112: parse error near `()'

I couldn't find anywhere that says there is any difference between using or not using the function keyword in a function definition. Why is that the solution in this case? (I tried in zsh and in bash)

like image 938
Fuoco Avatar asked Feb 15 '26 03:02

Fuoco


1 Answers

From Bash Reference Manual:

Aliases are expanded when a command is read, not when it is executed.

z is therefore expanded when the if statement is read, not when it is executed. So even if you unalias, the aliases are already expanded in your if statement (namely z() ... is expanded).

Adding function helps because aliases are expanded only when they are used as the first word. If you add function to your function declaration, nothing gets expanded.


Check this code that demonstrates the behavior of aliases inside a compound command:

#!/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

This snippet shows that the alias foo really gets expanded before execution. As a result, there is a function called bar declared, 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
} 

Bash Reference Manual explains the behavior of aliases in further detail and recommends the following:

To be safe, always put alias definitions on a separate line, and do not use alias in compound commands.

like image 72
PesaThe Avatar answered Feb 16 '26 16:02

PesaThe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!