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.
Best Answer
Sourcing an rc file rarely if ever works in practice, because people rarely write them to be idempotent. A case in point is your own, where you are prepending the same directory to the
fpath
path every time, which of course means that searching that path takes a little longer each time. No doubt this isn't the only place where you are doing that sort of thing, moreover.You also do not understand autoloading correctly. As the doco states, autoloading of a function without a body happens the first time that the function is executed. Obviously, if the function is already loaded, and thus has a body, it does not get loaded again.
You need to
unfunction
the function beforeautoload
ing it again.The sample
.zshrc
in the Z shell source contains anfreload()
function that does this very thing for all of the functions named as its arguments. It also doestypeset -U path cdpath fpath manpath
, notice.