Blacklist program from bash completion - bash

Fedora comes with "gstack" and a bunch of "gst-" programs which keep appearing in my bash completions when I'm trying to quickly type my git aliases. They're of course installed under /usr/bin along with a thousand other programs, so I can't just remove their directory from my PATH. Is there any way in Linux to blacklist these specific programs from appearing for completion?
I've tried the FIGNORE and GLOBIGNORE environment variables but they don't work, it looks like they're only for file completion after you've entered a command.

In 2016 Bash introduced an option for that. I'm reproducing the text from this newer answer by zuazo:
This is rather new, but in Bash 4.4 you can set the EXECIGNORE variable:
aa. New variable: EXECIGNORE; a colon-separate list of patterns that
will cause matching filenames to be ignored when searching for commands.
From the official documentation:
EXECIGNORE
A colon-separated list of shell patterns (see Pattern Matching) defining the list of filenames to be ignored by command search using
PATH. Files whose full pathnames match one of these patterns are not
considered executable files for the purposes of completion and command
execution via PATH lookup. This does not affect the behavior of the [,
test, and [[ commands. Full pathnames in the command hash table are
not subject to EXECIGNORE. Use this variable to ignore shared library
files that have the executable bit set, but are not executable files.
The pattern matching honors the setting of the extglob shell option.
For Example:
$ EXECIGNORE=$(which pytest)
Or using Pattern Matching:
$ EXECIGNORE=*/pytest

I don't know if you can blacklist specific files, but it is possible to complete from your command history instead of the path. To do that add the following line to ~/.inputrc:
TAB dynamic-complete-history

FIGNORE is for SUFFIXES only. It presumes for whatever reason that you want to blacklist an entire class of files. So you need to knock off the first letter.
E.g. To eliminate gstack from autocompletion:
FIGNORE=stack
Will rid gstack but also rid anything else ending in stack.

Related

Why "hash /bin/ls" does nothing?

Writing the command hash with some relative or an absolute path, whether to a file, directory or a to nowhere, it seems to do nothing.
$> hash /bin/ls
prints nothing ***
$> hash /path/to/nowhere
prints nothing ***
$> hash ../same/as/above
same thing ***
Why is that?
hash does a command search and remembers the resolved location. Part of command search is skipping it when there's a slash in the name*. Therefore, hash foo/bar does nothing.
Relevant POSIX spec:
hash:
The hash utility shall affect the way the current shell environment remembers the locations of utilities found as described in Command Search and Execution. [...] It shall add utility locations to its list of remembered locations
Command Search and Execution:
If the command name contains at least one <slash>, the shell shall execute the utility in a separate utility environment with actions equivalent to calling the execl() function
[...]
Once a utility [invoked without a slash] has been searched for and found (either as a result of this specific search or as part of an unspecified shell start-up activity), an implementation may remember its location and need not search for the utility again
* This is incidentally why you execute a script in the current dir with ./myfile: it's the shortest way to make the filename contain a slash.

macos: Bash shows commands instead files

Bash on my macOS shows all (probably) commands from $PATH when I double-TAB instead of files and folders. Same when I can use TAB once - it does not work at all. Commands like CD works fine.
I have no idea where the problem is - $PATH seems normal to me:
/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
~/.profile is this:
export PATH=/usr/local/php5/bin:$PATH
export PATH="$HOME/.yarn/bin:$PATH"
~/.bash_profile has only this:
export PATH="/usr/local/sbin:$PATH"
Same behavior under PhpStorm's terminal. So I believe, the problem is somewhere deeper.
Can you help, please?
Command-line completion in Bash is achieved through readline, a well-known utility library used by many GNU and other Open Source products.
Basic completion is using the {TAB} key, and the actual completion depends on context. If the text does not start with a special character then commands are listed if they match, only if commands don't match are filenames listed.
If completion can be achieved, enough characters are supplied to make the completion unique, then it will be done, otherwise a second {TAB} will give a list of possibilities.
From man bash (Completing):
Bash attempts completion treating the text as a variable (if the text
begins with $), username (if the text begins with ~), hostname
(if the text begins with #), or command (including aliases and
functions) in turn. If none of these produces a match, filename
completion is attempted.

is there a way to create symbolic link with wildcard

I have a script run.sh to which i created a number of symlinks like pf1, pf2 etc.
I want anything which starts with pf to map to this. Is there a way for me to create a symbolic link with a wildcard like "pf*" so that i don't have to create symbolic links for pf11, pf12 etc in the future?
In case bash is supported on your system (which is the case in most systems), you could do something like this -
bash -c "ln -s sourcedir/pf* targetdir/"
No. Symbolic link resolution is handled by the kernel whereas globbing is shell-specific.
If you store pf* in a symlink, the kernel will look for a file literally named pf*. You could theoretically readlink that and have your shell expand the read pattern, but then you might as well store the pattern in a regular file.
You can create symbolic links, which are broken (and become fully working, once the file is there), but a * wildcard will not be expanded before the files are there. Other expansions will work. Check it yourself using echo:
$ echo asdf*
asdf*
The wildcard is not expanded here, but you can use
$ echo asdf{1,2,3}
asdf1 asdf2 asdf3
and it is expanded as you would expect it.

Preprocess line before it is processed by bash

Is there a way to preprocess a line entered into bash in interactive mode before it is processed by bash?
I'd like to introduce some custom shorthand syntax to deal with long paths. For example, instead of writing 'cd /one/two/three/four/five', I'd like to be able to write something like 'cd /.../five', and then my preprocessing script would replace this by the former command (if a unique directory 'five' exists somewhere below /).
I found http://glyf.livejournal.com/63106.html which describes how to execute a hook function before a command is executed. However, the approach does not allow to alter the command to be executed.
There's no good way of doing this generally for all commands.
However, you can do it for specific commands by overriding them with a function. For your cd case, you can stick something like this in your .bashrc:
cd() {
path="$1"
[[ $path == "/.../five" ]] && path="/one/two/three/four/five"
builtin cd "$path"
}
In bash 4 or later, you can use the globstar option.
shopt -s globstar
cd /**/five
assuming that five is a unique directory.
The short answer is not directly. As you have found, the PROMPT_COMMAND environment variable allows you to issue a command before the prompt is displayed, which can allow for some very creative uses, e.g. Unlimited BASH History, but nothing that would allow you to parse and replace input directly.
What you are wanting to do can be accomplished using functions and alias within your .bashrc. One approach would be to use either findutils-locate or simply a find command to search directories below the present working directory for the last component in the ellipsed path, and then provide the full path in return. However, even with the indexing, locate would take a bit of time, and depending on the depth, find itself may be to slow for generically doing this for all possible directories. If however, you had a list of specific directories you would like to implement something like this for, then the solution would be workable and relatively easy.
To provide any type of prototype or further detail, we would need to know more about how you intent to use the path information, and whether multiple paths could be provided in a single command.
Another issue arises if the directory five is non-unique...

What does the bash command "rm *~" do?

Does the bash command rm *~ just remove files ending in tilde or is there a more advanced bash or gnu make pattern here? Google does not seem able to search for this two symbol combination. I found this in a Makefile clean: target.
Would gnu make ever create files with trailing ~'s using only the implicit rules?
The ~ (tilde) character has a special meaning in a path in two cases:
~user # the home directory of user
~/folder # folder inside your home directory
For the most part, that's it. The command you refer to does exactly what it looks like it does: removes files whose names end in a tilde. Text editors such as emacs save backup copies of files under filenames ending in tildes.
So, this command is probably used to remove these backup copies from the current directory (but not subdirectories). One reason why one would want to do so is if the directory will be copied to a web server, as server-side code (e.g. PHP files) can contain sensitive information such as passwords.
As you guessed, rm *~ just removes file with names ending with a tilde (~). Filenames ending with a tilde are usually backup files created by editors (in particular, emacs was one of the earlier editors to use this convention). After editing source code, it is common to have a number of these files left behind. This is why the clean target in the Makefile removes these.
Whether *~ is some special bash pattern is not relevant for most makefiles, as /bin/sh is used by default to execute make recipes. Only if SHELL is set in the makefile will a different shell be used.
An easy way to see make's implicit rules is to run make -p in a directory without a makefile. You will get an error saying no targets specified, but make will also print out the implicit rules it is using. If you grep this output for a tilde, you'll see there are no implicit rules that name files with it.
Nope, just what you said. Removes files ending with ~.
Edit -> the only special meaning the ~ character may have, is as short-hand for the the current user's home directory (as $HOME), but only in the beginning of a path.
I have used that command to erase files ending in "~". I think that there is no special escape character associated with the tilde symbol.
Yes to both
Actually, both of your possibilities are somewhat true.
There is no wildcard or special filename syntax associated with ~, unless it occurs at the beginning of a word.
But the filename pattern ending in tilde is produced automatically by the mv(1) and cp(1) programs on most linux distros1 if the -b (backup) option is specified and the target file exists. A make rule on such a system might contain a mv -b ... or cp -b ... command.
1. But not on the Mac or BSD.

Resources