customizing ZSH tilde expansion - linux

Context
This concerns ZSH tilde expansion (as in bash tilde expansion).
Example: ~http is expanded to /srv/http which is (roughly) the home for user http set in /etc/passwd.
What I want to do
I would like to configure ZSH in order to get any filename tilde expansion I wish.
This would be something like setting alias ~foo /foo/bar in my .zshrc.
What I do not want
I could create (as root) the user foo and set it’s home to /foo/bar but since any user should be able to do this, such a solution is not acceptable.
Also, this has nothing to do with the completion of ~ to $HOME.
Misc
This post discuss the nature of bash tilde expansion: Meaning of tilde in Linux bash (not home directory).
Thanks for any answer! ;-)

You could use either (or both) way:
hash -d foo=/foo/bar (hash builtin command)
foo=/foo/bar/
Now, we could use cd ~foo to change the current directory to /foo/bar, too.
It is known as “Static named directories” in zsh.
Note: when CDABLE_VARS shell option is active, we could do even cd foo rather than cd ~foo in the above example if the directory (/foo/bar in this example) exists.

Related

Using an environment variable in an rsync argument (dealing with quotes and escape character)

I want to use rsync with an --exclude command that looks like
rsync -av --exclude={"*.ext1","*.ext2", "*.ext3"} source target
(For some reason it is more handy to me to use such a syntax than using the --exclude-from=FILE syntax)
I would like to use an environement variable with rsync so that I could do
export toto='{"*.ext1","*.ext2", "*.ext3"}'
rsync -av --exclude=$toto source target
The problem is that it doesn't work the way I wish. When I use the automatic completion over the $toto variable it shows :
\{\"\*.ext1\",\"\*.ext2\",\ \"\*.ext3\"\}
It is actually what rsync understands and it gets totally confused.
Could anybody explain the rational behind this "quotes and escape characters" behavior?
This works:
MYEXCLUDE={'*.ext1','*.ext2'}
eval rsync -av --exclude=$MYEXCLUDE src dest
As already mentioned, {,} triggers brace expansion (btw, the space before ext3 in your example is a problem: it prevents brace exp).
And rsync's exclude option only accepts a single pattern (this means that everything can be understood by replacing rsync and echo).
By chance, when the 1st line sets the variable, brace expansion does not yet take place (echo $MYEXCLUDE shows : {*.ext1,*.ext2}).
And brace expansion happens after variable expansion. So the trick is to use eval to have the expansion just-in-time. To fully understand, compare the following 2 lines:
MYEXCLUDE=--exclude={'*.ext1','*.ext2'}
echo $MYEXCLUDE
eval echo $MYEXCLUDE
displays
--exclude={*.ext1,*.ext2}
--exclude=*.ext1 --exclude=*.ext2
If I understand your question, you cannot use brace expansion in the content of the environment variable.
Why? Brace Expansion is provided by your shell (bash). When rsync reads the content of --exclude=$toto no shell expansions are applied. A better approach would be to generate the list of files to exclude, redirect the output to a file and --exlcude-from=file where file would contain a single filename (or rule) on each line.
All of the quoting and escaping that is applied is from the attempt by rsync to make valid Unix filenames or patterns from {"*.ext1","*.ext2","*.ext3"}. (basically you have confused the heck out of rsync and it is doing its best with the mess you have given it).
When you have what should be a brace expansion stored in a variable, no shell expansions will be applied, quotes lose their meaning and rsync is left trying to sort out the mess.
Look at the --exclude, --exclude-from and --filter in rsync(1) - Linux manual page

What does this alias do in Red Hat Linux?

cd = 'cd !* ;set prompt="! $host `dirs` % "'
This alias is on a Red Hat Enterprise linux server I use commonly, and I can't figure out what it does for me. Any ideas?
I think it is meant to change the directory and display the previous directories in the prompt. !* is a bash history expansion meaning the previous command without the first word. E.g. if the previous command was cd project, then !* is project. See the History Expansion section in man bash, more specifically here.
dirs simply displays the stack of directories, and is a bash builtin, see the link for details.
However, in standard bash, you'd use export PS1=... instead of set prompt=, so perhaps the author of the alias meant it to be used in csh rather than bash, e.g. see here. The history expansion !* has the same semantics, e.g. see here for tcsh.
In csh aliasing, !* is the input the command received, e.g.
alias + emacs \!* & and running + somefile will open somefile using emacs and move it to the background.
Now to your line - it changes directory to whatever you give it (!*) and sets the prompt (the line written in your terminal before your writing zone) to the host name, path (as pwd) and %
Just a quick note - pay attention to the \ in my alias line - you must escape special characters in your alias and pay attention to this

How can I manipulate PWD directly?

I knew that there is a variable PWD which is changed when I type the command cd. But when I try to change it manually the current directory isn't changed. How to manipulate PWD directly?
POSIX describes PWD:
Assignments to this variable may be ignored.
Assignments are not special in bash, dash, ash, zsh or ksh, and the value will simply be overwritten next time the shell changes directory.
While other shells might support such a variable as well, the $PWD variable is generally specific to the bash shell.
The bash manual says:
PWD
The current working directory as set by the cd builtin.
Meaning it will be set when you use the cd builtin to change a directory but bash will not change the current directory when you set $PWD.
At least on my Mac and Linux machines running bash you can just overwrite the variable, so running PWD=/ will change your current directory (in the prompt) to /.
The variable PWD in bash is in control of the shell's working directory (again only the prompt) and you will see that the directory after the PWD= command has been changed but the pwd command isn't impacted by the change.
<0>xxxx#dhcp89-089-034:~$ PWD=/
<0>xxxx#dhcp89-089-034:/$ pwd
/Users/xxxx
Now the environment variable PWD is a bash prompt thing and is not the real working directory, nor is modifying it a useful thing to do unless you actually use the 'cd' command.

Why is a tilde in a path not expanded in a shell script?

I tried to get the Android Studio launcher (studio.sh) to use my manually installed Java (not the system-wide default Java). Since I already declared PATH and JAVA_HOME in my .bashrc file, I simply sourced that file in the shell script:
. /home/foobar/.bashrc
but for some reason, $JAVA_HOME/bin/java was still not recognized as an executable file by the script.
I added some logging and found out that JAVA_HOME was expanded as ~/install/java..., i.e. the tilde operator was not expanded into the home directory.
I did some searching, but couldn't find any reason why it was not expanded. Is tilde a Bash-specific feature (the script uses #!/bin/sh, and Linux Mint uses dash, not bash)? Does tilde not work in some circumstances?
I replaced ~ with $HOME in my .bashrc declaration, and then it worked, so HOME is known at runtime.
In the bash manual, note that brace expansion during parameter substitution, but not recursively:
The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and filename expansion.
This implies that any tilde (or parameter references or command substitution) stored unexpanded in a bash variable will not automatically resolve. Your JAVA_HOME variable contains a literal tilde, so bash will not expand it automatically.
It is likely that your fix worked because tilde expansion does not apply in quotes:
$ echo "~"
~
$ echo ~
/home/jeffbowman
...but parameter expansion like $HOME does occur in quotes. Replacing it with $HOME expands to your home directory during the assignment of JAVA_HOME.
FOO=~/bar # stores /home/jeffbowman/bar
FOO="~/bar" # stores ~/bar
FOO=$HOME/bar # stores /home/jeffbowman/bar
FOO="$HOME/bar" # stores /home/jeffbowman/bar
Though the better option is to ensure your assignment is correct, if you want to expand it manually, these SO questions have some good options:
"Tilde expansion in quotes"
"How to manually expand a special variable (ex: ~ tilde) in bash"

How do I set tilde expansion in zsh?

I am trying to achieve expansion of the tilde into the $HOME variable with zsh, and was wondering what best practice is in this case?
Basically what I want is that:
$ ls ~
on pressing TAB expands to
$ ls /home/myusername
where HOME=/home/myusername. The _tilde function usually does a ldap search, followed by named dirs, followed by the dir stack, none of which I need.
What is the best way to achieve this?

Resources