zsh using a variable in a command within a function - zsh

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.

Related

why all commands disappear when I run this function placed in my .zprofile

I have this function in my .zprofile, in my zsh I'm sourcing this file
function fd {
path=$(find ~/Documents ~/Documents/**/ -mindepth -maxdepth -type d | fzf)
if [ -z $path ]; then
return
fi
cd $path
}
then I can run fd as anyother command in my terminal, but before that I cannot run any other command like: ls,cd,etc...
~ $ fd
the fd command place me in the directory I selected
nodejs $ ls
once place in the new directory I cannot use anyother command
From the documentation:
In cases where there are two parameters with an upper- and lowercase form of the same name, such as path and PATH, the lowercase form is an array and the uppercase form is a scalar with the elements of the array joined together by colons. These are similar to tied parameters created via typeset -T. The normal use for the colon-separated form is for exporting to the environment, while the array form is easier to manipulate within the shell. Note that unsetting either of the pair will unset the other; they retain their special properties when recreated, and recreating one of the pair will recreate the other.
So by assigning a scalar value to the array variable path, you're also clobbering PATH, meaning zsh won't find any programs to run unless you give a path, not just a name. Use a different name for your variable.

zsh: create named file in place of argument?

realpath <<<'foo' fails "realpath: missing operand". I don't know what that means.
realpath <(<<<'foo') returns /proc/3443695/fd/pipe:[26244650] which I guess means it's creating a temporary pipe which will contain the string "foo".
Or maybe printf is more clear:
❯ printf "%q" <<<'foo' # no output
❯ printf "%q" <(<<<'foo')
/proc/self/fd/11%
The actual program I'm trying to call doesn't like either of those. I think I need an actual file.
I can do that in multiple commands by creating a file with mktemp and then writing to it, and then sending that off as the arg, but does zsh have any convenient syntax for doing this in-place? A 1-liner?
It looks like the =(list) process substitution should do what you want.
From the zshexpn man page:
If =(...) is used instead of <(...), then the file passed as an
argument will be the name of a temporary file containing the output
of the list process. This may be used instead of the < form for a
program that expects to lseek on the input file.
...
The temporary file created by the process substitution will be deleted when the function exits.
On my system, realpath =(<<<'foo') returns something like /private/tmp/zsh3YAdDx, i.e. the name of a temporary file that does indeed appear to be deleted after executing the command.
As a bonus, the documentation notes that in some cases the =(<<<...) form is optimized to execute completely in the current shell.

What does autoload do in 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.

kshell UNIX base directory

I am trying to understand the following lines of kshell script. Can anyone please explain why we need a dot and space in the third line?
#!/bin/ksh
export scriptDir=${0%/*}
. $scriptDir/version.profile
echo "JAVA_HOME_FOR_THIS_SCRIPT=$JAVA_HOME"
The . command, which can also be written as source, is a built-in command in ksh and other sh-derived shells. In this case, it executes the commands contained in $scriptDir/version.profile in the context of the current shell process.
Quoting the ksh man page:
. name [ arg ... ]
If name is a function defined with the function name reserved word
syntax, the function is executed in the current environment (as if it
had been defined with the name() syntax.) Otherwise
if name refers to a file, the file is read in its entirety and the commands are executed in the current shell
environment. The search path specified by PATH is used to find the
directory
containing the file. If any arguments arg are given, they become the positional parameters while processing the . command
and the original positional parameters are restored upon comple‐
tion. Otherwise the positional parameters are unchanged. The exit status is the exit status of the last command
executed.

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'

Resources