ZSH doesn't identify "*" as "all files" - shell

I use ZSH shell environment (with 'oh-my-zsh' plugin) on my Ubuntu 18.04 and I have noticed an issue with it. I think my ZSH shell doesn't identify * sometimes as "all files".
For example if I wanted to install all ROS packages with the prefix control I would enter the command : sudo apt-get install ros-melodic-control* and this should work and install all ROS Melodic packages starting with control from the main ROS repository. This thing worked when I used BASH earlier. But now when I do the same thing in ZSH it gives me the following error : zsh: no matches found: ros-melodic-control*
It would be great if someone helped me identify the exact problem.
TIA

It's the difference between zsh and bash. zsh never will run a command that contains wildcard pattern that doesn't match existing files. Bash will execute as is.
It also may fail differently. If look-up would return anything that matches the pattern, , eg. a file named ros-melodic-control.pdf, zsh will execute sudo apt-get install ros-melodic-control.pdf. NB: wildcards always are investigated by the shell.
Consider it be a paranoid shell for non-root users who primarily interact only with file system. To actually do what you want you have to use single quotes.
sudo apt-get install 'ros-melodic-control*'
Packages given as argument to apt-get aren't files. they are names in database which also contains their location (local cache in /var, disk, remote server or NFS source). SO you have to ensure they are passed as string if you use wildcards. Just with bash\sh\ksh you can get away without quotes if you have no matching files.
Adding quotes would pass enclosed text "as-is" to arguments of executed program, as a string. Therefore apt-get will receive string ros-melodic-control* and will search for packages that matches it. Without quotes shell will try to expand wildcards.

Related

Makefile docker command with single quote failling

With this in a Makefile:
single:
docker network inspect -f '{{ .IPAM }}' web-proxy
double:
docker network inspect -f "{{ .IPAM }}" web-proxy
make single fails with make: docker: Permission denied while make double succeeds. Both commands work if I input them directly in my bash.
It happens only since I upgraded to Ubuntu 22.04.1 (from the 22.04). I have docker 20.10.20, bash 5.1.16 and GNU Make 4.3
Any idea were it can come from ? From what I have read the Makefile doesn't care about quotes: https://stackoverflow.com/a/23332194
I don't know why updating your system made a difference, but this is almost certainly related to a bug in gnulib (that GNU make uses).
If you add a semicolon to the end of the docker command line in the single target I'll bet it will work again.
The bug is this: if some directory on your PATH contains a subdirectory with the name of a command you want to invoke, then if make attempts to run that command directly (without using a shell) it will fail because it tries to "run" that directory. So for example if you have /my/dir in PATH and the directory /my/dir/docker/. exists, you will get this error (for simple docker commands).
The "double" target works because (due to the {{) make's trivial parser decides that this command it not "simple enough" to parse directly, and it runs the shell to do it; the shell doesn't get confused by that directory.
You can (1) add the semicolon as above, or (2) figure out why some directory on your PATH contains a docker subdirectory and remove it.
The next release of GNU make (probably released by the end of the month) will fix this issue (includes a newer version of the gnulib module, with the fix).

How to provide shell completion with python a package? [duplicate]

I am writing a command line tool in python and using pip to distribute it. I have written some scripts (one for bash and one for zsh) to allow the tool to have tab completion. Is there a way to get pip to install these scripts when someone does a pip install?
For example:
I have a completion.bash file. When someone does
pip install mypackage
It will also source the bash file.
I'm pretty sure I can do this for linux and bash by putting the script in my data_files section in the setup.py script.
data_file=[
('/etc/bash_completion.d', ['bin/completion.bash'])
],
But how can I do this so it is platform and shell independent? I need it to work for mac/linux in both bash and zsh. If possible, even support windows.
Is this possible? And if so, how?
In case it matters, here is my bash script:
_foo_complete() {
COMPREPLY=()
local words=( "${COMP_WORDS[#]}" )
local word="${COMP_WORDS[COMP_CWORD]}"
words=("${words[#]:1}")
local completions="$(foo completer --cmplt=\""${words[*]}"\")"
COMPREPLY=( $(compgen -W "$completions" -- "$word") )
}
complete -F _foo_complete foo
I am currently installing it by just running source completion.bash
You are asking several different questions.
First, there's no cross-platform or cross-shell solution for defining custom shell-completions. The one you posted works for bash, but in tcsh, for example, you use tcsh's complete command, which works differently than bash's.
Second, sourcing the files which contain those completion-definitions at the time of pip install wouldn't do much good. The completions might work in that very session, but what you probably want is for them to take effect in future sessions (i.e. shell invocations) as well. For that, your files would have to be sourced each time the shell starts (e.g. from within user's .bashrc, in case of bash).
This measn that "installing" your files simply means placing them somewhere, and suggesting the users should source them from their respective dot-rc file. Even if you could, you shouldn't try to "force" it. Give your users the option to add that to their dot-rc file if they want.
The best approach would probably be to include in your package a completion-definitions file per supported shell, e.g. complete.bash, complete.tcsh, and god knows what for windows (sorry, I'm not a windows user).

How can I determine where an error message printed during shell startup comes from?

After uninstall Docker on Ubuntu, each time I start a bash shell window, it emits the following hint:
The program 'docker' is currently not installed. You can install it by typing:
sudo apt install docker.io
I have searched in .bashrc, .profile, .bash_profile, but didn't found any lines related to docker.
So, how can I get rid of those tips?
Run PS4=':${BASH_SOURCE}:$LINENO+' bash -x -l -i to log every command in your startup scripts, including which config file or script they came from. Search through that, and you'll find the individual command that's creating this error.
(Note that very new versions of bash ignore inherited values of PS4 when running as root for security reasons. But you're not using root as your primary account... right?)

How do Python commands, and any other commands, get automatically set up in terminal?

I was trying to set up a platform that used pip and I realized I had to install it using 'sudo easy_install'. I think easy_install is a python-enabled command that was set up in terminal after I installed Python.
How does it work that after you install something like Python, a command like easy_install is automatically activated with-in terminal that can call what easy_install does? Is there some sort of active list of commands that is updated for all terminal executions by programs that are installed?
Thanks.
All the places that the terminal will search for programs are in the PATH variable. To see which directories are in your path variable you can run the following in your terminal: echo $PATH. Note that paths are colon separated on most UNIX-based systems. As such, all programs contained in directories that are in the PATH variable are ones that can be run without specifying a relative/absolute path to them.

Putting links to scripts in my cygwin bin

I have made a few python scripts, but is there an easier way to run them? I am using cygwin.
python "C:\Users\Desk\Dropbox\scripts\wsort.py" > data11414_unsorted.txt < data11414_sorted.txt
I want something like this (not typing the path name or "python"):
wsort > data11414_unsorted.txt < data11414_sorted.txt
where wsort is a link to my real wsort.py
Add a
Shebang
to the script
#!/bin/python
then invoke like this
wsort.py > data11414_unsorted.txt < data11414_sorted.txt
First, your question has a Windows-style path (backslashes, beginning with C:) rather than a Cygwin path (/cygdrive/c/Users/Desk/Dropbox/scripts/wsort.py). That implies you're not actually using Cygwin, or if you are, you're ignoring a bunch of warnings.
The below assumes you're using Cygwin Bash (which should be what you get if you start Cygwin Terminal from the Start Menu) and Cygwin Python (which you've installed using Cygwin's setup.exe, not a Windows Python installer). If your not, you're making life more difficult for yourself than you need to.
That out the way, there's a bunch of steps you need to take:
First, make the script executable. Use the chmod command for that, from a Cygwin Bash shell:
chmod +x /cygdrive/c/Users/Desk/Dropbox/scripts/wsort.py
Second, tell the system how to execute it. Add the following line to the top of the script:
#!/bin/python
(That's a "shebang". Python sees it as a comment, so doesn't do anything with it, but Cygwin and other Linux-like systems will use that line to see which program to run the script with. In this case, Python.)
Third, make sure your line endings are correct. Cygwin expects Linux line endings and will fail without them. This may not be a problem, but there's no harm in doing this. Run the following command:
dos2unix /cygdrive/c/Users/Desk/Dropbox/scripts/wsort.py
At this point, you'll be able to call the script by specifying the full path to it in Cygwin. You can't yet run it without specifying where the script is explicitly.
The fourth step is making sure the script is "in your path", ie in one of the folders where Cygwin looks for scripts to run. There are lots of ways to do this, but the most sensible is probably to just add your scripts directory to your path. The following command will add your scripts directory to your path whenever you start a new Cygwin session:
echo 'PATH="/cygdrive/c/Users/Desk/Dropbox/scripts:$PATH"' >>~/.bashrc
You will need to restart your Cygwin terminal for that to take effect, however.
At that point, you'll be able to run the script in Cygwin just by typing wsort.py (and thus use it with redirections and so forth as in your question).
Finally, to be able to call it simply as wsort, there's a number of options. The obvious one is just renaming the file. More usefully (and without copying the file or doing anything liable to break with Dropbox syncing things), try creating an alias:
echo 'alias wsort=wsort.py' >>~/.bashrc
Again, you'll need to restart your Cygwin terminal for that to take effect.
Maybe use an alias ?
alias wsort = "Command_Used"

Resources