'have' keyword for bash completion - bash

Is have a keyword in bash? Or do bash completion scripts use a language that is not bash?
have gcc &&
_gcc()
{
It is common. See: grep "have .* &&" /etc/bash_completion.d/*
I could not find any information on the bash completion tutorials I've seen and I could not find any information in man bash. It's also difficult to google "have". Where do I find documentation on this?
I'm guessing it has to do with making sure that there gcc exists in the PATH?
edit: yes. /etc/bash_completion contains:
have()
{
unset -v have
# Completions for system administrator commands are installed as well in
# case completion is attempted via `sudo command ...'.
PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin type $1 &>/dev/null &&
have="yes"
}

have and _have are just two functions defined in the base bash_completion file. Between the two, they form a wrapper around the built-in type command to determine if a particular command/program available.
# This function checks whether we have a given program on the system.
#
_have()
{
# Completions for system administrator commands are installed as well in
# case completion is attempted via `sudo command ...'.
PATH=$PATH:/usr/sbin:/sbin:/usr/local/sbin type $1 &>/dev/null
}
# Backwards compatibility for compat completions that use have().
# #deprecated should no longer be used; generally not needed with dynamically
# loaded completions, and _have is suitable for runtime use.
have()
{
unset -v have
_have $1 && have=yes
}

Related

Where is the mkvirtualenv --python command-line argument documented?

I was taking a course on django and when creating a virtual environment, they used the following command:
mkvirtualenv envname --python=/usr/bin/python3.8
I've no idea what the additional arguments mean, and just wanted an explanation. Many thanks! Apologies if this is something simple - just starting out.
The mkvirtualenv command is a shell-script that usually lives at /usr/local/bin/virtualenvwrapper.sh.
The source-code for which can be found on GitHub.
In it, references to the --python command-line parameter can be found in the bash-function function mkvirtualenv {.
This is the relevant part ([...] denotes code I removed for readability): it loops over all provided command-line arguments:
function mkvirtualenv {
[...]
while [ $i $tst $# ]
do
a="${in_args[$i]}"
case "$a" in
[...]
-p|--python)
i=$(( $i + 1 ));
interpreter="${in_args[$i]}";
interpreter="$(virtualenvwrapper_absolutepath "$interpreter")";;
[...]
esac
i=$(( $i + 1 ))
done
The above code handles the --python command-line argument.
The --python (double-dash) and -p (single-dash) arguments are treated as synonyms.
It's used to set the path to the actual Python interpreter/engine that your new virtual-environment will use.
This is needed because you can have multiple, different, and incompatible Python installs side-by-side on the same physical computer, but you need to select a single unambiguous (and correctly versioned) Python executable to run your Python programs.

Get path of currently executing shell script run with busybox

In ConTeXt standalone, the file tex/setuptex contains code that needs the current path of tex/setuptex (which is usually e.g. $HOME/context, /opt/context, etc.). The code looks like this:
# this resolves to path of the setuptex script
# We use $0 for determine the path to the script, except for:
# * bash where $0 always is bash; here we use BASH_SOURCE
# * ksh93 where we use ${.sh.file}
# Thanks to Vasile Gaburici and Alessandro Perucchi for reporting this
# * http://www.ntg.nl/pipermail/ntg-context/2008/033953.html
# * http://www.ntg.nl/pipermail/ntg-context/2012/068658.html
if [ z"$BASH_SOURCE" != z ]; then
SCRIPTPATH="$BASH_SOURCE"
elif [ z"$KSH_VERSION" != z ]; then
SCRIPTPATH="${.sh.file}"
else
SCRIPTPATH="$0"
fi
One usually runs this to update the current environment with an assortment of environment variables needed to run ConTeXt, with . path/tex/setuptex.
In BusyBox (on e.g. Alpine Linux), the $SCRIPTPATH is /, and it's not apparent what the correct way to get the path would be. Adding this line to the script:
echo "SCRIPTPATH $0 : $1 : $2"
yields:
SCRIPTPATH sh : :
Similarly env yields nothing with setuptex.
So I'm not certain where to start.
How does one replicate the *sh functionality one ordinarily uses to obtain the path of the currently executing script?
From Henri Menke (by email) -
Standard POSIX sh has no way to reliably detect sourced invocation of a script.
BusyBox uses POSIX sh underneath and thus suffers from the same limitation. See
StackOverflow for detail: how to get script directory in POSIX sh?

Run scripts without typing the extension

I use git-bash on windows.
Is there a way to make it run windows batches (.cmd) without typing file extensions?
And the same question about *.sh files. I prefer putting bash scripts into .sh to distinguish them in the explorer easier.
PS. Aliases or proxy-scripts without extension are not welcomed.
Hopefully, there is some geek's way to do this. So, still looking for an answer...
You could any flavour of bash (cmder, git-bash, ms bash, cygwin, gnu-utilities…) which will let you use any unix command without extension, even if stored as a .exe.
Take attention to the CR/LF, and use dos2unix at each shell script creation to prevent misinterpretation of line ending.
You won't be able to use .sh file without extension… but to create an alias for each script :
$ alias myscript="/usr/local/bin/myscript.sh"
Put them in your ~/.bashrc
First of all a bit of content about file extension:
A filename extension is an identifier specified as a suffix to the
name of a computer file. The extension indicates a characteristic of
the file contents or its intended use. A file extension is typically
delimited from the filename with a full stop (period), but in some
systems it is separated with spaces.
Some file systems implement filename extensions as a feature of the
file system itself and may limit the length and format of the
extension, while others treat filename extensions as part of the
filename without special distinction.
Filesystems for UNIX-like operating systems (opposed to DOS/Windows) do not separate the
extension metadata from the rest of the file name. The dot character
is just another character in the main filename, and filenames can have
multiple extensions, usually representing nested transformations, ...
Regarding your shell scripts:
So basically in Unix file extension are not important/not mandatory so you can directly omit them. If for any reason you want to keep them (and I believe that you should) then you can define an alias to them. (refer to https://askubuntu.com/questions/17536/how-do-i-create-a-permanent-bash-alias)
You must also keep in mind the EOL char ('\n' vs '\r\n' that differ between Unix and Windows.
Regarding your windows batches, you can not run them directly in a Unix like environment so you will not be able to run them at the same time from your git bash except if you use a tool like GH4W (github generate ssh key on windows) or use git-cmd.bat (What is the exact meaning of Git Bash?)
Maybe I've found a solution that requires some additional coding. Instead of insulting the user when typing a non-existent command, you could try to rewrite the code so it executes a command by adding '.sh' or '.cmd'.
GitGub: insult-linux-unix-bash-user-when-typing-wrong-command
Also do a search for the command_not_found_handle. This is available on most Linux systems and might or might not be available for git-bash.
This is something I was looking for myself and following on from FithAxiom's answer, have found a solution. It feels ugly, because it basically means overriding the "command not found" handling, and searching for the file ourselves. But it does achieve the desired effect.
This is a modification of an answer given in another thread. You add this to your .bashrc file. You can modify it to suit your needs (and improvements are also welcome).
command_not_found_handle()
{
cmd=$1
shift
args=( "$#" )
IFS=:
for dir in $PATH; do
if [ -f $dir/$cmd.cmd ]; then { "$dir/$cmd.cmd" "${args[#]}"; return; }
elif [ -f $dir/$cmd.ps1 ]; then { powershell.exe -file "$dir/$cmd.ps1" "${args[#]}"; return; }
elif [ -f $dir/$cmd.sh ]; then { "$dir/$cmd.sh" "${args[#]}"; return; }
fi
done
# Replicate standard "command not found" error
echo "bash: $1: command not found" >&2
return 127
}
You can create a file ~/.bashrc and override the command not found handler. Bash let you handle it. Read it for more info.
In .bashrc file you can update the command_not_found_handle function provided by bash.
command_not_found_handle() {
unset -f command_not_found_handle
command="$1".sh
shift
exec "$command" "$#"
}

How is the command suggestion implemented in the bash shell?

gk#Jarvis:~$ sudi
No command 'sudi' found, did you mean:
Command 'sudo' from package 'sudo-ldap' (universe)
Command 'sudo' from package 'sudo' (main)
sudi: command not found
I have currently implemented a simple 'Did you mean..?' for plain English words which works as follow:
If user enters 'Kack', check the alphabets around every alphabet in the word on a QWERTY keyboard and substitute them one by one. (e.g. here they would be J,L,M,I,O for 'K'; Q,W,S,Z,X for 'a' and so on)
Return the word with the most probability (the user entered word itself too if that is the case) as the most likely word based on training on a corpus of text.
How is the code-suggestion implemented in the linux command line?
bash does not implement the suggestion logic; it is in a function defined as part of your bash initialization file, and it was put there by your distribution (Ubuntu/Debian, at a guess).
bash provides the mechanism for implementating such a function: when it attempts to execute a command, and the command is not found, it invokes the function command_not_found_handle, if it is defined.
On my machine (an Ubuntu variant), that function is defined as follows:
$ type command_not_found_handle
command_not_found_handle is a function
command_not_found_handle ()
{
if [ -x /usr/lib/command-not-found ]; then
/usr/lib/command-not-found -- "$1";
return $?;
else
if [ -x /usr/share/command-not-found/command-not-found ]; then
/usr/share/command-not-found/command-not-found -- "$1";
return $?;
else
printf "%s: command not found\n" "$1" 1>&2;
return 127;
fi;
fi
}
(And /usr/lib/command-not-found exists, is executable, and is a Python script.)
From the Bash man page:
COMMAND EXECUTION
[…]
If the name is neither a shell function nor a builtin, and contains no slashes, bash searches each element of the PATH for a directory containing an executable file by that name. Bash uses a hash table to remember the full pathnames of executable files (see hash under SHELL BUILTIN COMMANDS below). A full search of the directories in PATH is performed only if the command is not found in the hash table. If the search is unsuccessful, the shell searches for a defined shell function named command_not_found_handle. If that function exists, it is invoked with the original command and the original command's arguments as its arguments, and the function's exit status becomes the exit status of the shell. If that function is not defined, the shell prints an error message and returns an exit status of 127.
Let's try this out:
$ foobar
bash: foobar: command not found
$ function command_not_found_handle { echo "I'm so sorry, what is '$1'?"; }
$ foobar
I'm so sorry, what is 'foobar'?
Your shell initialization code might install a more useful command_not_found_handle. You would typically find such code in the system-wide configuration in /etc/bash.bashrc or a file sourced by it. Your distribution might install a handler there to invoke an external program that queries the distribution's package manager for the command or “similar” commands. For your Ubuntu, this would be implemented in the command-not-found package.
The default configuration files shipped by distributions are usually kept very general so the function might check whether the command-not-found binary is installed and, if so, call it or otherwise print a simple error message.
function command_not_found_handle {
if [ -x /usr/bin/command-not-found ]
then
/usr/bin/command-not-found "$1"
else
echo "$1: Command not found" >&2
return 127
fi
}
This way, the configuration file does not have to be changed if the command-not-found package is installed or removed again later.
I don't know how that program for Ubuntu is implemented but typically, such a tool would have a list of all known commands and find the most similar one. It might then check whether that program is installed and, if not, check what package provides it and suggest installing that.
Searching for “similar text” is usually done by computing the edit distance between two strings. Taking into account how likely mistyping a given letter is, given the current keyboard layout, would be a very smart addition.

Navigating to directory in ZSH (bash)

I'm using Oh-My-ZSH to create some ailises and functions for easing my repetitive work load.
I need to navigate from anywhere in my computer to my Frontend directory. This is what I have:
frontend(){
cd ~/Desktop/Work/Frontend
cd $1
}
Now this works well when I type frontend or frontend myProject, however, all my project folders are postfixed by something like .m, .tablet, etc.
How can I write things that:
Will let me automatically navigate to a folder that is followed by .something
When there are multiple options (like project.m and project.tablet) will prompt me with options similar to if you hit tab in your terminal and are given multiple options for autocomplete.
I hope my question makes sense.
Thanks.
Find a zsh solution first, followed by a bash solution.
Update: Turns out that a zsh implementation (based on builtin compctl) is much simpler than the bash implementation (based on builtin complete).
Save the code of interest to a file (e.g., frontend) and source it (e.g., . ./frontend); either interactively or, preferably, from your bash/zsh profile.
Once in place, auto-completion of subdirectory names in ~/Desktop/Work/Frontend will work as follows:
Type, for instance, frontend myProject and press TAB.
myProject is then prefix-matched against the names of the subdirectories in ~/Desktop/Work/Frontend:
If there's only 1 match, myProject will instantly expand to the full subdirectory name.
Otherwise, a beep sounds to indicate that there are multiple matches:
zsh: The names of all matching subdirectories are listed right away.
bash: Press TAB again to list the names of all matching subdirectories
Continue typing until the prefix match is unambiguous, then press TAB again.
Note: In bash, to also only require pressing TAB once to list multiple matches, add the following to your shell profile bind "set show-all-if-ambiguous on".
zsh solution:
# Define the shell function.
frontend(){
cd ~/Desktop/Work/Frontend/"${1:-}"
}
# Tell zsh to autocomplete directory names in the same directory as
# the function's when typing a command based on the shell function.
compctl -/ -W ~/Desktop/Work/Frontend frontend
bash solution:
Note: complete -o dirnames doesn't take an argument, unfortunately - it always auto-completes for the current directory. Thus, a custom shell function that returns the potential matches, combined with -o filenames, is required.
# Define the main shell function.
frontend(){
local BASEDIR=~/Desktop/Work/Frontend
cd "$BASEDIR/${1:-}"
}
# Define the custom completion function.
_frontend_completions() {
local BASEDIR=~/Desktop/Work/Frontend
# Initialize the array variable through which
# completions must be passed out.
COMPREPLY=()
# Find all matching directories in the base folder that start
# with the name prefix typed so far and return them.
for f in "$BASEDIR/${COMP_WORDS[COMP_CWORD]}"*; do
[[ -d $f ]] && COMPREPLY+=( "$(basename "$f")" )
done
}
# Tell bash to autocomplete directory names as returned by the
# _frontend_completions() helper functoin when typing a command
# based on the main shell function.
complete -o filenames -F _frontend_completions frontend fe
I strongly recommend you use AutoJump
But if you must, maybe you want to use alias
like in your ~/.zshrc add:
alias fend='cd path/to/frontend'

Resources