Weird behaviour with zsh PATH - zsh

I just encourage a weird problem with zsh today.
My environment is Mac OS X Yosemite, zsh 5.0.5 (x86_64-apple-darwin14.0)
In .zshrc, I have manually set the PATH variable to something like
export PATH="$PATH:~/.composer/vendor/bin"
Try echo $PATH in terminal, the result is as expected (contained ~/.composer/vendor/bin). Then try executing a binary from ~/.composer/vendor/bin, It'll always return me "zsh: command not found" error.
Try switching to bash, echo $PATH is also as expected, have the same result as zsh shell.
Try executing a binary from ~/.composer/vendor/bin, no problem found. Seem the PATH var is acting well on the bash shell.
What's wrong with my zsh shell?
Thanks

Try using $HOME instead of ~. In many situations, shells do not expand ~ when you expect them to and it is usually better to use $HOME. ~ is really only intended to be a short cut for interactive use. (The only case I can recall where ~ was preferred was in a .gitalias, where ~ was expanded and variables were not.)

Type rehash to pick-up $PATH changes.
From the zsh user guide:
The way commands are stored has other consequences. In particular, zsh
won't look for a new command if it already knows where to find one. If
I put a new ls command in /usr/local/bin in the above example, zsh
would continue to use /bin/ls (assuming it had already been found). To
fix this, there is the command rehash, which actually empties the
command hash table, so that finding commands starts again from
scratch. Users of csh may remember having to type rehash quite a lot
with new commands: it's not so bad in zsh, because if no command was
already hashed, or the existing one disappeared, zsh will
automatically scan the path again; furthermore, zsh performs a rehash
of its own accord if $path is altered. So adding a new duplicate
command somewhere towards the head of $path is the main reason for
needing rehash.
EDIT However #WilliamPursell could be onto something with his comment:
note that "composer" != ".composer"

Related

WSL PATH environment variable is incorrect / zsh config not loaded [duplicate]

I am using Ubuntu via WSL 2.0 on Windows 10 and would like to run Texlive from the Windows command line. To do so I prepended the Texlive folder to the path in /etc/environment (I also tried a number of other locations eg. $HOME/.bashrc):
C:\Users\scott\Documents>wsl echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:...
C:\Users\scott\Documents>wsl
scott#SCOTT-PC:/mnt/c/Users/scott/Documents$ echo $PATH
/usr/local/texlive/2020/bin/x86_64-linux:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:...
Why is there a difference between these two paths? Is it possible to change the first PATH variable?
To be honest, when I first looked at this question, I thought it would be an easy answer. Oh how wrong I was. There are a lot of nuances to how this works.
Let's start with the fairly "easy" part, though. The main difference between the first method and the second:
wsl by itself launches into a login (and interactive) shell
the shell launched with wsl echo $PATH is neither a login shell nor an interactive shell
So the first will source both login scripts (e.g. ~/.profile) and interactive startup scripts (e.g. ~/.bashrc). The second form does not get to source either of these.
You can see this a different way (and get to the solution) with the following commands:
wsl -e bash -c 'echo $PATH'
wsl -e bash -li -c 'echo $PATH'
The -li forces bash to run as a login and interactive shell, thus sourcing all of the applicable startup scripts. And, as #bovquier points out in the comments, a single quote is needed here to prevent PowerShell from interpolating the $ before it gets to Bash. That, or escape it.
You should be able to run TeX Live the same way, just replacing the "echo $PATH" with the startup command you need for TeX Live.
A second option would be to create a script that both adds the path and runs the command, and just launch that script through wsl /path/to/script.sh
That said, I honestly don't think that your current login/interactive PATH is coming from /etc/environment. In my testing, at least, /etc/environment has no use in WSL, and that's to be expected. /etc/environment is only sourced by PAM modules, and with no login check performed by WSL, there's no reason to invoke PAM in either the wsl nor the wsl echo $PATH commands.
I'd expect that you still have the PATH setting in ~/.bashrc or somewhere similar), and that's where the shell is picking it up from at the moment.
While this isn't necessarily critical to understanding the answer, you might also wonder, if /etc/environment isn't used for setting the default (non-login, non-interactive) path in WSL, what is? The answer seems to be that it is hard-coded into the init that starts up WSL. That init is also what appends the Windows path (assuming you don't have that feature disabled in /etc/wsl.conf).

Deleting a command from zsh history

I'm writing a shell history application, and I'm experiencing issues with deleting a command from zsh history. When I was developing bash compatibility, I used readline to manipulate history - I first deleted command from readline's history, then wrote the history to a file. After the program exits, if the history was manipulated, then I do history -r and I get the expected behavior. Since zsh does not use readline and its history command significantly differs from the one that can be found on bash, I'm left with no other choice but to manipulate the history file directly. However, when I do that, the changes are not reflected until the shell is restarted. I have tried to use zsh's equivalent of history -r, which is supposed to be fc -R, but I did not get the expected results. While the command gets deleted from history after running that, pressing the up arrow (in order to go back to the previous command, which should be the one that invoked the program) brings me to a seemingly random command in the history.
I'd appreciate it if someone explained the odd behavior I'm experiencing. Any pointers in the right direction are also welcome.
I have solved this problem in my Zsh history editing plugin zsh-hist by not touching the histfile directly, but instead doing everything through Zsh's own mechanisms:
Set $HISTORY_IGNORE locally to a pattern that matches the contents of the entries you want to delete.
Use fc -W to write the history to file. Because of HISTORY_IGNORE, the unwanted entries will not be written.
Use fc -p to create a new history from file.
Zsh documentation:
$HISTORY_IGNORE parameter
fc builtin command

Way to get jupyter server root directory

Consider following:
$ cd /home/mydir
$ jupyter notebook --port=8888
In plain English, I am running jupyter server from /home/mydir directory.
Is there a simple way to get this directory from within a notebook regardless if it's a R notebook or a Python notebook or whatever? Maybe there is some magic command or variable?
NOTE: getwd() is not an answer as it returns directory of a current notebook but not the jupyter server root.
I have a similar problem and found your post, although I don't see a viable solution yet. Eventually I did found a solution, although it works only because I only care about Linux and I only care about Python. The solution is this magic line:
J_ROOT = os.readlink('/proc/%s/cwd' % os.environ['JPY_PARENT_PID'])
(I put it in a module in my PYTHONPATH so that I can easily use it in any Python notebook.) See if it is good for you.
Remember that your iPtyhon is just a Python module, so you can execute any valid Python code in a cell. So, if you started your notebook and haven't executed any directory changes in your code, you should be able to retrieve your cwd with the following in a cell:
import os
os.getcwd()
But furthermore, you can execute shell commands in cells, so you can retrieve other information in the cell. For example:
!which jupyter
should give you the path to your jupyter executable.
Which then leads you to running something like:
!jupyter --paths
which should give you something similar to:
​
config:
/Users/yourdir/.jupyter
/usr/local/etc/jupyter
/etc/jupyter
data:
/Users/yourdir/Library/Jupyter
/usr/local/share/jupyter
/usr/share/jupyter
runtime:
/Users/yourdir/Library/Jupyter/runtime
Frankly I'm surprised that all this time later there is still no built-in way to do this. I have used Isaac To's solution on Linux but recently had to make a jupyter notebook portable to Windows as well. Simply using os.getcwd() is fragile because a cell using it to set your JUPYTER_ROOT_DIRECTORY can potentially be called again, after you have changed your working directory.
Here is what I came up with:
try:
JUPYTER_ROOT_DIRECTORY
except NameError:
JUPYTER_ROOT_DIRECTORY = os.getcwd()
I put it in one of the first couple cells with the initial import statements. If the cell gets called again, it will not re-set the variable value because the variable exists and does not throw an exception.
It should be noted that unlike Isaac To's solution it sets the value to the directory the current .ipynb was run from, which is not necessarily the same directory as the top-level dir the user can access in the left hand file pane.
My suggestion is to use an intuitive approach.
Create a new folder within the Jupyter environment with a very unique name, for example, T246813579.
You can now locate the Jupyter working path by searching in your file explorer. For example, you can use the Windows Explorer in order to locate your new folder.
The expected result should look something like this:
C:\Users\my_user_name\JupyterHome\T246813579
The answer from #Isaac works well for Linux, but not all systems have /proc. For a solution that works on macOS and Linux, we can use shell commands, taking advantage of the ! shell assignment syntax in Jupyter:
import os
JPY_ROOT = ! lsof -a -p {os.environ['JPY_PARENT_PID']} -d cwd -F n | tail -1 | cut -c 2-
JPY_ROOT = JPY_ROOT[0]
print(JPY_ROOT) # prints Jupyter's dir
Explanation:
Get the process ID (pid) of the current jupyter instance with os.environ['JPY_PARENT_PID']
Call lsof to list the process's open files, keeping only the current working dir (cwd)
Parse the output of lsof using tail and cut to keep just the directory name we want
The ! command returns a list, here having only one element
Alternate Version
To save the os import, we could also use shell commands to get the PID. We could also do the subsequent string wrangling in python, rather than calling tail and cut from the shell, as:
JPY_ROOT = ! lsof -a -p $(printenv | grep JPY_PARENT_PID | cut -d '=' -f 2) -d cwd -F n
JPY_ROOT = JPY_ROOT[2][1:]

Changing interpreter bash script

Is there any way for changing the interpreter in the middle of a bash script
For instance start with:
#!/bin/bash
Later change to:
#!$drush_location
The reason is because I want to use bash to resolve the location of drush using bash and then pass that var in as an interpreter
You will need to write two scripts and use the first (bash) one to launch the second (drush).
There are other ways to accomplish this, but they are all basically fancy ways of doing the above. For example you could use a here-doc to cram a script contained as a string in your first script into stdin on drush and have it execute that, or even write a temporary file and execute that as a script, but you have to run two processes somehow, you can't change the interpreter on the fly.
Really the thing to do would be to fix your environment so that it can find drush. Then you can use:
#!/usr/bin/env drush
As the hashbang for your drush script. If your system evn can't find it, then fix your search paths until it can!

Zsh wants to autocorrect a command, with an _ before it

I just started using Zsh lately for some of the integrated support in the shell prompt for my Git status etc.
When I type in:
ruby -v
to confirm the version of ruby I'm running, Zsh asks if I want to change the command to _ruby. Well after saying no at the prompt and the command completing as expected I continue to get the question at the prompt after confirming my command is correct.
I'm assuming there is a completion file or something of the sort.
Thanks
Update:
The shell is no longer trying to complete _ruby, it stopped responding after closing the shell a few times some how.
I tried to clean the file up several times but there is a "opts" variable that is 50 or more lines long and the lines are all ran together, some lines more than 150 characters. Maybe I could email an attachment to you if you still want to see it.
I sincerely apologize for the messy post.
This is command autocorrection, activated by the correct option. It has nothing to do with completion. You're seeing _ruby because zsh thinks there is no ruby command and it offers _ruby as the nearest existing match.
If you've just installed ruby, it's possible that zsh has memorized the list of available command earlier, and it won't always try to see if the command has appeared in between. In that case, run hash -rf. Future zsh sessions won't have this problem since the ruby command already existed when they started.
Sometimes, when you change your PATH, zsh forgets some hashed commands. The option hash_listall helps against this. As above, if you can force zsh to refresh its command cache with hash -rf.
You could make an alias:
alias ruby='nocorrect ruby'
It's what I did when zsh kept asking me if I meant .meteor when I typed meteor because auto-correct is still useful from time to time.
I find the autocorrect feature can get annoying at times. So I do in my ~/.zshrc,
DISABLE_CORRECTION="true"
I had the same problem even when the command is not installed.
I can solve it using the CORRECT_IGNORE variable in my .zshrc
# OPTs to enable
setopt HASH_LIST_ALL
setopt CORRECT
# Zsh variable to determine what to ignore,
# in this case everything starting with _ or .
CORRECT_IGNORE="[_|.]*"
I hope it helps to you or anyone with this issue
Sometime ago after an update, I got command auto-correction enabled which I don't want. If the same happened to you and you want to revert it, in the ~/.zshrc file you'll have make it:
# Uncomment the following line to enable command auto-correction.
ENABLE_CORRECTION="false"
or comment it as per bellow:
# Uncomment the following line to enable command auto-correction.
# ENABLE_CORRECTION="true"
Just a note, on my zsh (version 5.7.1 on macOS), the DISABLE_CORRECTION didn't work.
I saw in my .zshrc file the following two lines, which I then commented out
setopt CORRECT
setopt CORRECT_ALL
That did it for me.

Resources