Difference between bash (shell) function syntaxes? [duplicate] - bash

For example:
Bash-Prog-Intro-HOWTO
function foo() {}
I make search queries in info bash and look in releted chapters of POSIX for function keyword but nothing found.
What is function keyword used in some bash scripts? Is that some deprecated syntax?

The function keyword is optional when defining a function in Bash, as documented in the manual:
Functions are declared using this syntax:
name () compound-command [ redirections ]
or
function name [()] compound-command [ redirections ]
The first form of the syntax is generally preferred because it's compatible with Bourne/Korn/POSIX scripts and so more portable.
That said, sometimes you might want to use the function keyword to prevent Bash aliases from colliding with your function's name. Consider this example:
$ alias foo="echo hi"
$ foo() { :; }
bash: syntax error near unexpected token `('
Here, 'foo' is replaced by the text of the alias of the same name because it's the first word of the command. With function the alias is not expanded:
$ function foo() { :; }

The function keyword is necessary in rare cases when the function name is also an alias. Without it, Bash expands the alias before parsing the function definition -- probably not what you want:
alias mycd=cd
mycd() { cd; ls; } # Alias expansion turns this into cd() { cd; ls; }
mycd # Fails. bash: mycd: command not found
cd # Uh oh, infinite recursion.
With the function keyword, things work as intended:
alias mycd=cd
function mycd() { cd; ls; } # Defines a function named mycd, as expected.
cd # OK, goes to $HOME.
mycd # OK, goes to $HOME.
\mycd # OK, goes to $HOME, lists directory contents.

The reserved word function is optional. See the section 'Shell Function Definitions' in the bash man page.

Related

Setting environment variables with .sh script not working under zsh : how to convert a function [duplicate]

I've noticed that it's usual task to check global variables like PATH, GOPATH and etc. That's why I want to write small function so instead of typing a lot of letters
echo $PATH
I can type just
e PATH
The function itself should be very simple
function e() {
echo $($1) # it produces the error "command not found"
}
But the problem is how to substitute a variable to get the content of PATH?
P.S. I'm using zsh
The traditional (POSIX) notation to handle this uses the eval command, which many will warn you against:
e() {
eval echo \"\$$1\"
}
In bash, however, you can use variable indirection:
function e() {
printf '%s\n' "${!1}"
}
And in zsh, which you added as a tag after my initial answer, indirection is handled differently:
function e() {
printf '%s\n' "${(P)1}"
}
This uses a Parameter Expansion Flag which you can read about in man zshexpn.
P This forces the value of the parameter name to be interpreted as a
further parameter name, whose value will be used where appropriate.

Using alias in bash function

I have defined an alias like so:
alias X="path/to/program"
and I have a function defined like this:
doX() { X -flag "$1"; }
I put these in my .bashrc file, and when I open bash, I get a syntax error near unexpected token '-flag'. At this point, the alias has been set, but the function has not, due to this error. If I run
doX() { X -flag "$1"; }
at this point, it works. I have tried putting this into a file and sourcing it after I set the alias in the .bashrc file, but it is giving me the same results.
How can I fix this? Is there a way to define the alias AND the function in the .bashrc so that they are both set when I open bash?
Aliases are not usually available in scripts. If you want to have a function use an alias, consider making the alias itself a function:
X() { path/to/program "$#"; }
doX() { X -flag "$1"; }

Why bash functions use round brackets, if those are never filled with arguments?

The function definition syntax for various languages is:
C (the godfather of all scripting languages):
func_type myfunc_c (arg_type arg_name , ...)
{
/* arguments explicitly specified */
}
TCL:
proc myfunc_tcl {arg1 arg2 args} {
# arguments explicitly specified
}
Perl:
sub myfunc_perl {
# no arguments explicitly specified && no round brackets used
}
Python:
def myfunc_python(arg1, arg2):
# arguments explicitly specified
Bash:
function myfunc_bash () {
# arguments NEVER explicitly specified
# WHY using round brackets?
}
Why using round brackets in bash?
Parentheses are optional. From Bash Reference Manual --> 3.3 Shell Functions:
Functions are declared using this syntax:
name () compound-command [ redirections ]
or
function name [()] compound-command [ redirections ]
This defines a shell function named name. The reserved word function
is optional. If the function reserved word is supplied, the
parentheses are optional. The body of the function is the compound
command compound-command (see Compound Commands). That command is
usually a list enclosed between { and }, but may be any compound
command listed above. compound-command is executed whenever name is
specified as the name of a command. When the shell is in POSIX mode
(see Bash POSIX Mode), name may not be the same as one of the special
builtins (see Special Builtins). Any redirections (see Redirections)
associated with the shell function are performed when the function is
executed.
So these are equivalent:
function hello {
echo "hello there"
}
hello () {
echo "hello there"
}
In Bash, functions can access global variables normally, so that the approach is slightly different from other languages. Normally, there is no need to use return because there is no value to catch.
See an example. Here, we have a global variable myvar containing a value. In the functions mytest and mytest_inner we are changing its value. However, in one case the value affects the global environment, whereas in the other does not.
In mytest we change the value and it affects the main block. In mytest_inner we do the same, but the value is just changed locally, in the sub-shell running in the function.
#!/bin/bash
function mytest {
echo "mytest -> myvar: $myvar"
((myvar++))
}
function mytest_inner () {
(
echo "mytest_inner -> myvar: $myvar"
((myvar++))
)
}
myvar=$1
mytest
echo "main -> myvar: $myvar"
mytest_inner
echo "main -> myvar: $myvar"
Let's run it:
$ ./myscript.sh 20
mytest -> myvar: 20
main -> myvar: 21
mytest_inner -> myvar: 21
main -> myvar: 21
Why using round brackets in bash?
Actually, they're not needed, at least not in my version.
$ foo() { echo 'foo!' ; }
$ foo
foo!
$ function bar { echo 'bar!' ; }
$ bar
bar!
$ function baz() { echo 'baz!' ; }
$ baz
baz!
$ bash --version | head -n 1
GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)
man bash:
Shell Function Definitions
A shell function is an object that is called like a simple command and executes a compound
command with a new set of positional parameters. Shell functions are declared as follows:
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. The body of
the function is the compound command compound-command (see Compound Commands
above). That command is usually a list of commands between { and }, but may be any
command listed under Compound Commands above. compound-command is executed when-
ever name is specified as the name of a simple command. Any redirections (see RE-
DIRECTION below) specified when a function is defined are performed when the func-
tion is executed. The exit status of a function definition is zero unless a syntax
error occurs or a readonly function with the same name already exists. When exe-
cuted, the exit status of a function is the exit status of the last command exe-
cuted in the body. (See FUNCTIONS below.)

Why doesn't the shell require formal arguments in the function signature?

Why does a shell not need parameters for functions?
Example of addition function below that adds num1 and num2.
I mean that you don't write the parameters inside the () of the line function addition().
addition()
{
echo $(($num1+$num2))
}
If your question is why does this function work, how does it get the num1 and num2 variables?", the answer is: it gets those variables from the parent context, for example this will echo hello Jack:
hello() {
echo hello $name
}
name=Jack
hello
You can rewrite the function to use positional arguments like this:
hello() {
echo hello $1
}
hello Jack
As per why not write variable names in the function declaration: that's just the way bash is made. From the man page:
Shell Function Definitions
A shell function is an object that is called like a simple command and
executes a compound command with a new set of positional parameters.
Shell functions are declared as follows:
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. The body of the function is the com‐
pound command compound-command (see Compound Commands above).
....
That is, the function declaration must be in one of the explained forms, with () (no variable names in between) mandatory when not using the function keyword, and optional otherwise.
From the manpage:
When a function is executed, the arguments to the function become the positional parameters during its execution. The special parameter # is updated to reflect the change. Special parameter 0 is unchanged.
In CS terms, bash functions don't use formal parameters because the positional parameters are always set when (and only when) you apply the function:
$ ##
$ # Show the function arguments
$ showParams() {
> printf '%s\n' "$#"
$ }
$ showParams 1 2 3
1
2
3
$ set -- 1 2 3
$ printf '%s\n' "$#"
1
2
3
$ showParams # Here you can see the parameters in the shell are not set in the function application:
$
…but this also means bash does not support keyword arguments.
You may also wish to read the section under Positional Parameters in the manpage.
Shell functions don't need prototypes because
All variables are string variables. They get converted to numbers as needed e.g. when doing arithmetic. (BTW, declaring variables as integers is a shell extension not found in POSIX).
The number of parameters passed is known when the function is called and available as $# so the function body can deal with variadic functions.

Passing argument to alias in bash [duplicate]

This question already has answers here:
Make a Bash alias that takes a parameter?
(24 answers)
Closed 5 years ago.
Is it possible to do the following:
I want to run the following:
mongodb bin/mongod
In my bash_profile I have
alias = "./path/to/mongodb/$1"
An alias will expand to the string it represents. Anything after the alias will appear after its expansion without needing to be or able to be passed as explicit arguments (e.g. $1).
$ alias foo='/path/to/bar'
$ foo some args
will get expanded to
$ /path/to/bar some args
If you want to use explicit arguments, you'll need to use a function
$ foo () { /path/to/bar "$#" fixed args; }
$ foo abc 123
will be executed as if you had done
$ /path/to/bar abc 123 fixed args
To undefine an alias:
unalias foo
To undefine a function:
unset -f foo
To see the type and definition (for each defined alias, keyword, function, builtin or executable file):
type -a foo
Or type only (for the highest precedence occurrence):
type -t foo
to use parameters in aliases, i use this method:
alias myalias='function __myalias() { echo "Hello $*"; unset -f __myalias; }; __myalias'
its a self-destructive function wrapped in an alias, so it pretty much is the best of both worlds, and doesnt take up an extra line(s) in your definitions... which i hate, oh yeah and if you need that return value, you'll have to store it before calling unset, and then return the value using the "return" keyword in that self destructive function there:
alias myalias='function __myalias() { echo "Hello $*"; myresult=$?; unset -f __myalias; return $myresult; }; __myalias'
so..
you could, if you need to have that variable in there
alias mongodb='function __mongodb() { ./path/to/mongodb/$1; unset -f __mongodb; }; __mongodb'
of course...
alias mongodb='./path/to/mongodb/'
would actually do the same thing without the need for parameters, but like i said, if you wanted or needed them for some reason (for example, you needed $2 instead of $1), you would need to use a wrapper like that. If it is bigger than one line you might consider just writing a function outright since it would become more of an eyesore as it grew larger. Functions are great since you get all the perks that functions give (see completion, traps, bind, etc for the goodies that functions can provide, in the bash manpage).
I hope that helps you out :)
Usually when I want to pass arguments to an alias in Bash, I use a combination of an alias and a function like this, for instance:
function __t2d {
if [ "$1x" != 'x' ]; then
date -d "#$1"
fi
}
alias t2d='__t2d'
This is the solution which can avoid using function:
alias addone='{ num=$(cat -); echo "input: $num"; echo "result:$(($num+1))"; }<<<'
test result
addone 200
input: 200
result:201
In csh (as opposed to bash) you can do exactly what you want.
alias print 'lpr \!^ -Pps5'
print memo.txt
The notation \!^ causes the argument to be inserted in the command at this point.
The ! character is preceeded by a \ to prevent it being interpreted as a history command.
You can also pass multiple arguments:
alias print 'lpr \!* -Pps5'
print part1.ps glossary.ps figure.ps
(Examples taken from http://unixhelp.ed.ac.uk/shell/alias_csh2.1.html .)
To simplify leed25d's answer, use a combination of an alias and a function. For example:
function __GetIt {
cp ./path/to/stuff/$* .
}
alias GetIt='__GetIt'

Resources