You're mixing up scripts and functions.
Making a script
A script is a standalone program. It may happen to be written in zsh, but you can invoke it from anywhere, not just from a zsh command line. If you happen to run a script written in zsh from a zsh command line or another zsh script, that's a coincidence that doesn't affect the script's behavior. The script runs in its own process, it doesn't influence its parent (e.g. it can't change variables or the current directory).
Your code accomplishes a standalone task which can be invoked from anywhere and doesn't need to access the state of the shell that runs it, so it should be a script, not a function.
A script must be an executable file: chmod +x /path/to/script
. It must start with a shebang line to let the kernel know what program to use to interpret the script. In your case, add this line to the top of the file:
#!/usr/bin/env zsh
Put the file in a directory that is listed in the $PATH
variable. Many systems set up either ~/bin
or ~/.local/bin
in a user's default PATH
, so you can use these. If you want to add another directory, see http://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path
When you type a command name that isn't an alias, a function or a builtin, the shell looks for an executable file of that name in $PATH
and executes it. Thus you don't need to declare the script to the shell, you just drop it in the right place.
Making a function
A function is code that runs inside an existing shell instance. It has full access to all the shell's state: variables, current directory, functions, command history, etc. You can only invoke a function in a compatible shell.
Your code can work as a function, but you don't gain anything by making it a function, and you lose the ability to invoke it from somewhere else (e.g. from a file manager).
In zsh, you can make a function available for interactive sessions by including its definition in ~/.zshrc
. Alternatively, to avoid cluttering .zshrc
with a very large number of functions, you can use the autoloading mechanism. Autoloading works in two steps:
- Declare the function name with
autoload -U myfunction
.
- When
myfunction
is invoked for the first time, zsh looks for a file called myfunction
in the directories listed in $fpath
, and uses the first such file it finds as the definition of myfunction
.
All functions need to be defined before use. That's why it isn't enough to put the file in $fpath
. Declaring the function with autoload
actually creates a stub definition that says “load this function from $fpath
and try again”:
% autoload -U myfunction
% which myfunction
myfunction () {
# undefined
builtin autoload -XU
}
Zsh does have a mechanism to generate those stubs by exploring $fpath
. It's embedded in the completion subsystem.
- Put
#autoload
as the first line of the file.
- In your
.zshrc
, make sure that you fully set fpath
before calling the completion system initialization function compinit
.
Note that the file containing a function definition must contain the function body, not the definition of the function, because what zsh executes when the function is called is the content of the file. So if you wanted to put your code in a function, you would put it in a file called extract
that is in one of the directories on $fpath
, containing
#autoload
if [ -f $1 ]; then
…
If you want to have initialization code that runs when the function is loaded, or to define auxiliary functions, you can use this idiom (used in the zsh distribution). Put the function definition in the file, plus all the auxiliary definitions and any other initialization code. At the end, call the function, passing the arguments. Then myfunction
would contain:
#autoload
my_auxiliary_function () {
…
}
myfunction () {
…
}
myfunction "$@"
P.S.
7z x
works on most archive types.
~/.alias
and source~/.alias
in my~/.zshrc
. No need forfpath
here. – pfnuesel Mar 09 '18 at 08:02