What does autoload do in zsh? - zsh

I wasn't able to find a documentation for the widely used autoload command in zsh. Does anybody can explain it in plain English?
A bit more specific: What does autoloading of modules mean, for example in this line:
autoload -Uz vcs_info
What does it do?
I've tried autoload --help, man autoload, googling - no success. Thanks!

The autoload feature is not available in bash, but it is in ksh (korn shell) and zsh. On zsh see man zshbuiltins.
Functions are called in the same way as any other command. There can be a name conflict between a program and a function. What autoload does is to mark that name as being a function rather than an external program. The function has to be in a file on its own, with the filename the same as the function name.
autoload -Uz vcs_info
The -U means mark the function vcs_info for autoloading and suppress alias expansion. The -z means use zsh (rather than ksh) style. See also the functions command.
Edit (from comment, as suggested by #ijoseph):
So it records the fact that the name is a function and not an external program - it does not call it unless the -X option is used, it just affects the search path when it is called. If the function name does not collide with the name of a program then it is not required. Prefix your functions with something like f_ and you will probably never need it.
For more detail see http://zsh.sourceforge.net/Doc/Release/Functions.html.

autoload tells zsh to look for a file in $FPATH/$fpath containing a function definition, instead of a file in $PATH/$path containing an executable script or binary.
Script
A script is just a sequence of commands that get executed when the script is run. For example, suppose you have a file called hello like this:
echo "Setting 'greeting'"
greeting='Hello'
If the file is executable and located in one of the directories in your $PATH, then you can run it as a script by just typing its name. But scripts get their own copy of the shell process, so anything they do can't affect the calling shell environment. The assignment to greeting above will be in effect only within the script; once the script exits, it won't have had any impact on your interactive shell session:
$ hello
Setting 'greeting'
$ echo $greeting
$
Function
A function is instead defined once and stays in the shell's memory; when you call it, it executes inside the current shell, and can therefore have side effects:
hello() {
echo "Setting 'greeting'"
greeting='Hello'
}
$ hello
Setting 'greeting'
$ echo $greeting
Hello
So you use functions when you want to modify your shell environment. The Zsh Line Editor (ZLE) also uses functions - when you bind a key to some action, that action is defined as a shell function (which has to be added to ZLE with the zle -N command).
Now, if you have a lot of functions, then you might not want to define all of them in your .zshrc every time you start a new shell; that slows down shell startup and uses memory to store functions that you might not wind up calling during the lifetime of that shell. So you can instead put the function definitions into their own files, named after the functions they define, and put the files into directories in your $FPATH, which works like $PATH.
Zsh comes with a bunch of standard functions in the default $FPATH already. But it won't know to look for a command there unless you've first told it that the command is a function.
That's what autoload does: it says "Hey, Zsh, this command name here is a function, so when I try to run it, go look for its definition in my FPATH, instead of looking for an executable in my PATH."
The first time you run command which Zsh determines is autoloaded function, the shell sources the definition file. Then, if there's nothing in the file except the function definition, or if the shell option KSH_AUTOLOAD is set, it proceeds to call the function with the arguments you supplied. But if that option is not set and the file contains any code outside the function definition (like initialization of variables used by the function), the function is not called automatically. In that case it's up to you to call the function inside the file after defining it so that first invocation will work.

Related

zsh using a variable in a command within a function

In .zsh, in my .zshrc file I'd like to set up a function to cd to a directory I input, but using an existing variable to write the common ~/path/to/parent/directory/$input
I've been unable to find out what the correct syntax is for this particular usage. For example, I want to enter
goto mydir
and execute a cd to ~/path/to/parent/directory/mydir
But I get an error: gt:cd:3 no such file or directory ~/path/to/parent/directory/mydir even though that directory exists.
This is the variable declaration and function I am trying:
export SITESPATH="~/path/to/parent/directory"
function gt(){
echo "your site name is $#"
echo "SITESPATH: " $SITESPATH "\n"
cd $SITESPATH/$#
}
It makes no difference if I use the above, without quotes, or "cd $SITESPATH/$#" with quotes.
I don't see the point in using $# in your function, since you expect only one argument. $1 would be sufficient.
The problem is in the tilde which is contained in your variable SITEPATH. You need to have it expanded. You can either do it by writing
export SITESPATH=~/path/to/parent/directory
when you define the variable, or inside your function by doing a
cd ${~SITESPATH)/$1
A third possibility is to turn on glob_subst in your shell:
setopt glob_subst
In this case, you can keep your current definition of $SITESPATH, and tilde-substitution will happen automatically.

Zsh not recognizing alias from within function

Here's my .zshrc:
alias inst="adb install -r -d"
notify() {
start=$(date +%s)
"$#"
notify-send "\"$(echo $#)\" took $(($(date +%s) - start)) seconds to finish"
}
When I run the command notify inst app.apk, I get an error:
notify:2: command not found: inst
0.00s user 0.00s system 0% cpu 0.001 total
Can anyone shed some light on why this doesn't work, and hopefully a way to make it work?
When the shell processes commands, among other things (e.g. PATH search) it will check to see if the first token/argument (whitespace delimited) belongs to an alias loaded in the current environment. If the alias you are trying to substitute is not the first token, the substitution will not happen. If your alias happens to not be the name of an executable on the PATH or current directory, the error will propagate back up to a command not found.
Since your question is about the Z Shell, zsh actually provides a lesser-known feature called global aliasing. If an alias is declared with the -g flag, zsh will make the appropriate substitution for not only the first token, but any token, regardless of order.
alias -g inst="adb install -r -d" should do the trick.
Keep in mind that this is a zsh only feature for portability reasons, and make sure that whatever script your writing has a shebang line that invokes the zsh shell: #!/usr/bin/env zsh
I would also recommend to not use zsh global aliasing in important or production scripts. For personal use, it is perfectly fine.
Don't use alias for scripting
First, according to the advanced bash scripting guide:
In a script, aliases have very limited usefulness.
So you may consider not using alias but a function for instance (still from the same page, 2 paragraph lower):
Almost invariably, whatever we would like an alias to do could be accomplished much more effectively with a function.
A hacky solution
If this is for a quick script for yourself using aliases you have in your .zshrc, there is still a way out.
alias foo='echo hello'
bar() {
`alias "$#" | cut -d\' -f2`
}
bar foo # => hello
Alias replacement
from alias man page:
The first word of each simple command, if unquoted, is checked to see if it has an alias. If so, that word is replaced by the text of the alias. The alias name and the replacement text can contain any valid shell input, including shell metacharacters, with the exception that the alias name can not contain `='.
The first word of the replacement text is tested for aliases, but a word that is identical to an alias being expanded is not expanded a second time. This means that one can alias ls to "ls -F", for instance, and Bash does not try to recursively expand the replacement text.
Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt .
The rules concerning the definition and use of aliases are somewhat confusing. Bash always reads at least one complete line of input before executing any of the commands on that line. Aliases are expanded when a command is read, not when it is executed. Therefore, an alias definition appearing on the same line as another command does not take effect until the next line of input is read. The commands following the alias definition on that line are not affected by the new alias. This behavior is also an issue when functions are executed. Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a compound command. As a consequence, aliases defined in a function are not available until after that function is executed. To be safe, always put alias definitions on a separate line, and do not use alias in compound commands.

typeset functions location

When I use the command typeset -f in ksh, a list of functions with their definition is displayed in stdout.
I tried to search where those functions are defined, but I couldn't find any hint about them. Can anyone help me finding them?
EDIT
I'm just learning the use of the typeset command, typing man typeset game me nothing (no manual entry for typeset).
In order to define functions that will be displayed using typeset -f, we need to define a function and export it using typeset -xf.
Functions can be declared in the .profile, or files called from .profile or put in a dir that is referenced by the FPATH variable (and proabably other places too). Read your man ksh carefully for the order of files that are processed on startup. Search for the 'Invocation', 'Files', and 'Functions' sections.
Also, there are a group of default functions that ksh sets up. So please edit your question to show the function names that your concerned with.
IHTH
Shells don't keep a record of where functions (or aliases, or variables, etc...) are defined. Conceptually, and notwithstanding interactive usage features like shell history, shells read commands from input one at a time, execute them, and then forget them. Sometimes those commands come from interactive input, sometimes they come from scripts. Sometimes they have side effects like defining a function in the shell's environment, but the shell still doesn't remember the command or its position in the shell's input stream after it's finished executing it.

How do I keep functions/variables local to my zshrc?

Any variable that I declare in my zshrc is available in the shell as an environment variable. I don't want this to happen.
I tried putting the variables in a function and setting them as local, but then the function is available outside of the zshrc.
How can I make it so what happens in my zshrc stays in my zshrc?
If you're using a recent version of zsh you can use an anonymous function:
function () {
local xyz=abc
# whatever
}
The function will be automatically executed and then thrown away, it exists only for scoping purposes.
This works for any sourced file, not only zshrc.
They are available, but they are not exported so scripts launching from command-line don’t get these variables. If your .zshrc looks like
function zshrc()
{
local VAR=1
# Do stuff
}
zshrc
and you then never want to launch zshrc function again you can just do
unfunction zshrc
afterwards.
If you do not prefix a variable with the word local it will remain until you do one of the following:
Open a new terminal window.
Run exec zsh or exec bash depending on your shell. This just clears out your local variables that were not assigned with the word local.
Avoid this
method_name(){
a=11
echo $a
}
Correct Example
method_name(){
local a=11
echo $a
}
This variable is scoped to the function name method_name and only available inside of the function when called (and not afterwards).
If you want direct access to that local variable you can set it this way
local z=11
And call it this way
echo $z
Additionally, environment variables are different from local variables
Depending on your shell and needs, you may use .bash_profile or .bashrc or .zshrc etc. to store functions and aliases.
View this reference for more on environment variables and their respective shells
Also read this to understand how to set environment variables on the command line using shell expansions
You can quickly view environment variables with env or printenv
The convention is to use UPPERCASE
To temporarily set an environment variable (stored until you close the terminal)
export A=11 or export B="11 is part of this string"
Assuming you have opened a new terminal window or sourced .zshrc or .bashrc or whichever you are using you can now use this environment variable until you close your terminal session. Note: do not use $ when setting, but do use $ when referencing the variable.
Examples
echo "A is equal to: $A and that is pretty nice"
echo "$A"
How to source a file
source ~/path/to/file/filename
Example
source ~/.bash_profile
To set an environment variable (until you remove it or set it again)
Use the code above but place it in your ~/.bash_profile or ~/.zshrc or other respective file. Save the file and source it.
Example
export B="11 is part of this string"
You now can view it with
env
To remove that environment variable, remove it from the file and again source the file.
To temporarily remove an environmental variable, use unset
Example
unset B
Note there is no $ when unsetting.
To set environment variables from the command line
export BLABLA="environment variable set from the command line, saved in file for later use"
Check the file you are sending it to, it may not start on a new line, it might have been concatenated to your last line which was some other function, alias or other.
This is not a fully comprehensive answer, but it is a great step in the right direction. It shows how scope in a terminal shell can be set, used and removed.
There is apparently a bash convention to name 'private' functions with double underscore .. of course they are not actually private . I am using this convention in my .zshrc.
function __comment()
{
curr=`pwd`
echo "$curr $*"
}
__comment 'Here is a Comment'

how provide data for zsh completion system

Is there any standard way of providing list of program switches, so it would be possible for zsh to determine possible completions? Or must it provided directly to zsh developers and only they can add completions to zsh?
Your first stop should be man zshcompsys.
Then you could look at an example such as /usr/share/zsh/functions/Completion/Unix/_vim.
The Z-Shell doesn't automatically know what possible switches work with which binary files. As far as I'm aware, there's no standard way for a shell to determine this.
ZSH works by using completion functions, which are written for specific programs. For example, zsh ships with completion functions for ssh, cvs, git, ls, etc.
If you want to look at these completion functions, you can. If you're in a zsh shell, echo $fpath to see the function path that zsh uses to load completion functions. There's a directory called /usr/local/share/zsh/4.3.17/function (location may vary for distributions / zsh versions), which has a bunch of files beginning with _ - _ssh, _cvs, etc. Those are the completion functions.
One massive clue that these are not generated automatically comes from a comment in the _ssh completion function that ships with 4.3.17 (may or may not be in your specific version):
# Completions currently based on OpenSSH 5.9 (released on 2011-09-06).
#
# TODO: update ssh-keygen (not based on 5.9)
# TODO: sshd, ssh-keyscan, ssh-keysign
Providing completion for the Z-Shell: using fpath
You can write your own completion functions, and developers can write functions for their programs and submit to the zsh developers for inclusion. Z-Shell completion functions go somewhere on the fpath.
If the program, say foobar, follows GNU conventions for options, you can use:
compdef _gnu_generic foobar
Otherwise you can write your own functions. The easiest to use IMO is _describe.
Create a file _foobar with contents:
#compdef foobar
cmds=(
'--one:option one'
'--four:option four'
'no-slashes:options do not need to start with a slash'
)
_describe 'foobar' cmds
Place the file somewhere in your $fpath
Add compdef _foobar foobar
If You are using ruby with the optparse package there is a hidden flag --*-completion-zsh=NAME that will output all that is needed for the completion for that ruby program. Store it in a file named _NAME somewhere in your $fpath and it will work. NAME should be exactly what your program/script is called.
I use a folder in my $HOME for that and added the path to $fpath but that required an additional line in my .zshrc:
autoload -U ~/.completion/*(:t)

Resources