How to activate FULL $var completion - bash

under Debians BASH I can do the following:
foo=/path/to/some/file
cat $f[TAB]
As you can see, I can autocomplete the variable $foo by pressing TAB.
Under Ubuntu (11) the behaviour is not the same (I've activated '/etc/bash_completion' in my bashrc).
This is working:
echo $f[TAB]
This one is not working:
cat $f[TAB]
Can anyone give me a hint, how to get the last one working?

You can remove the completion specification provided for cat by /etc/bash_completion by doing:
complete -r cat
or you can see if modifying the completion options provided in /etc/bash_completion work in the version of Bash you're using:
complete -F _longopt -o filenames -o bashdefault cat
If you're using the latest patched version of Bash 4.2, there is an option that may help (I haven't tried it):
shopt -s direxpand

Related

command substitution not working in alias?

I wanted to make an alias for launching a vim session with all the c/header/makefiles, etc loaded into the buffer.
shopt -s extglob
alias vimc="files=$(ls -A *.?(c|h|mk|[1-9]) .gitconfig [mM]akefile 2>/dev/null); [[ -z $files ]] || vim $files"
When I run the command enclosed within the quotations from the shell, it works but when run as the alias itself, it does not. Running vimc, causes vim to launch only in the first matched file(which happens to be the Makefile) and the other files(names) are executed as commands for some reason(of course unsuccessfully). I tried fiddling around and it seems that the command substitution introduces the problem. Because running only the ls produces expected output.
I cannot use xargs with vim because it breaks the terminal display.
Can anyone explain what might be causing this ?
Here is some output:
$ ls
Makefile readme main.1 main.c header.h config.mk
$ vimc
main.1: command not found
main.c: command not found
.gitignore: command not found
header.h: command not found
config.mk: command not found
On an related note, would it be possible to do what I intend to do above in a "single line", i.e without storing it into a variable files and checking to see if it is empty, using only the output stream from ls?

bash script yields a different result when sourced

Could you help me, why this script works when sourced (or even directly on console) and does not work on a script?
I have checked and in any case I'm using the same bash in /bin/ and always 4.4.19(1)-release (checked with $BASH_VERSION).
Moreover I tried removing shebang but nothing changes.
#!/bin/bash
fname=c8_m81l_55.fit
bname=${fname%%+(_)+([0-9]).fit}
echo $bname
GIving these results:
test:~$ ./test.sh
c8_m81l_55.fit
test:~$ . ./test.sh
c8_m81l
Bash does not recognize +(pattern) syntax unless extglobs are enabled, and they are disabled by default. Apparently your bash setup enables them in interactive sessions; that's why your script works only when sourced in an interactive shell.
To fix that, either enable extglobs within the script by this command:
shopt -s extglob
Or use an alternative that works irrespective of shell's interactiveness:
bname=$(sed 's/__*[0-9][0-9]*\.fit$//' <<< $fname)
# with GNU sed it'd look like:
bname=$(sed -E 's/_+[0-9]+\.fit$//' <<< $fname)

Can nano detect file type without extension by the shebang to have proper syntax highlighting?

I see nano cannot detect a file type by a shebang (hashbang) line like
#!/usr/bin/env bash
or similar.
Vim copes with this task w/o problems.
Is there a way to make it work for nano?
P.S. Created github issue.
P.P.S. Even nano 4.2 version doesn't support this. (compiled from sources on CentOS7)
There is a bug in nano syntax highlighting detection for .sh files, which I have found present in nano 4.8 and NOT present in nano 2.9.8, whereby a #! line with /env in it will not detect any shell except sh.
I have even found the specific commit which fixed it: https://git.savannah.gnu.org/cgit/nano.git/commit/?id=6a3ba2ab501c138c7ee1e72d2a65cea77342a43c
Annoyingly, at time of writing this is affecting .sh color syntax highlighting in up-to-date nano from the package manager on up-to-date current LTS version of Ubuntu (20.04).
To fix it, you have to replace your /usr/share/nano/sh.nanorc with the same file from a newer (or older!) version of nano.
The current one from https://git.savannah.gnu.org/cgit/nano.git/tree/syntax/sh.nanorc works fine.
I've decided to make simple wrapper for that.
#!/usr/bin/env bash
####################################################
# Find file type and set syntax highlight for nano #
####################################################
set -o pipefail
set -o errexit
set -o nounset
#set -o xtrace
# Determine path to nano binary file
if [[ -f /usr/local/bin/nano ]]; then
nano_bin=/usr/local/bin/nano
elif [[ -f /usr/bin/nano ]]; then
nano_bin=/usr/bin/nano
else
echo 'error: Sorry, nano binary file not found neither by path /usr/local/bin/nano nor /usr/bin/nano.' > /dev/stderr
exit 2
fi
# check if syntax highlight argument already passed
if ! echo ${#} | grep -E '(-Y|--syntax)' > /dev/null; then
# fetch interpreter name
syntax_type=$(head -1 bin/cli | grep '#!' | awk '{match($0,"([a-z]+)$",a)}END{print a[0]}')
if [[ -n "${syntax_type}" ]]; then
# map a file interpreter onto syntax type like BASH into SH
case "${syntax_type}" in
bash)
syntax_type=sh
;;
esac
nano_argument="--syntax=${syntax_type}"
fi
fi
${nano_bin} ${nano_argument:-} ${#}
Installation
Simple option for bash
Copy the code into ~/.nano-wrap.sh
nano ~/.nano-wrap.sh
Add an alias into your .bashrc file:
echo 'alias nano="bash ~/.nano-wrap.sh" >> ~/.bashrc'
And reload it:
source ~/.bashrc
J
I've found that saving the file (Ctrl+O, type name, Enter) will cause nano to auto-detect the file type from the shebang and then syntax-highlight the file appropriately from then on.

Can I get zsh-style path interpolation/completion in windows git bash?

On my Linux machine I have used a nice zsh feature to complete multipart paths with a single tab press:
$ cd /h/p<tab>
->
$ cd /home/plato
I am currently developing in a Windows environment, using Git Bash.
Is there any way I can replicate this behavior, complete paths without typing out the first parts completely?
You can use the complete command to run a custom function when you press tab. Add these lines to .bashrc:
# Solution written by izabera on freenode. tyvm!
#.bashrc
myfunction () {
local path oldpath ng=$(shopt -p nullglob)
shopt -s nullglob
printf -v path %q "${COMP_WORDS[COMP_CWORD]}"
oldpath=$path
path=${path//\//*/} path=${path#\*}
eval "COMPREPLY=($path*)"
if (( ${#COMPREPLY[#]} == 0 )); then COMPREPLY=("$oldpath"); fi
eval "$ng"
}
complete -D -F myfunction -o bashdefault -o default -o filenames
edit:
the -D flag was introduced in bash 4.1, the current bash version used by git-bash is 3.1.1.
The flag sets the completion function as the default, for any command that does not already have a completion function defined.
On 3.1.1, the best you can do is configure specific commands:
#.bashrc
myfunction () { ... }
complete -F myfunction -o bashdefault -o default -o filenames cd ls cp mv node npm
Disclosure: I am the author of the project
This project enables the desired completion in bash: https://github.com/sio/bash-complete-partial-path
It works just fine on Windows with Git bash. Hope you'll find it useful

bash completion of makefile target

Suppose I have a simple makefile like:
hello:
echo "hello world"
bye:
echo "bye bye"
Then in bash I want something like:
make h < tab >
so it can complete to
make hello
I found a simple way like creating empty files hello and bye but I'm looking for something more sophisticated.
Add this in your ~/.bash_profile file or ~/.bashrc file
complete -W "\`grep -oE '^[a-zA-Z0-9_.-]+:([^=]|$)' ?akefile | sed 's/[^a-zA-Z0-9_.-]*$//'\`" make
This searches for a target in your Makefile titled 'Makefile' or 'makefile' (note the capital ? wildcard in ?akefile) using grep, and pipes it over to the complete command in bash which is used to specify how arguments are autocompleted. The -W flag denotes that the input to the complete command will be a wordlist which is accomplished by passing the results of grep through sed which arranges it into the desirable wordlist format.
Caveats and gotchas:
Your make file is named 'GNUMakefile' or anything else other than 'Makefile' or 'makefile'. If you frequently encounter such titles consider changing the regular expression ?akefile accordingly.
Forgetting to source your ~/.bash_profile or ~/.bashrc file after making the changes. I add this seemingly trivial detail since, to the uninitiated it is unfamiliar.
For any change to your bash files to take effect, source them using the command
source ~/.bashrc
or
source ~/.bash_profile
PS. You also now have the added ability to display the possible make targets by pressing [Tab] twice just like in bash completion. Just make sure you add a space after the command make before typing [Tab] twice.
This answer from 2010 is outdated - the project mentioned here seems to have been discontinued.
Could this be what you're looking for?
http://freshmeat.net/projects/bashcompletion/
make [Tab] would complete on all
targets in Makefile. This project was
conceived to produce programmable
completion routines for the most
common Linux/UNIX commands, reducing
the amount of typing sysadmins and
programmers need to do on a daily
basis.
There's a useful package called bash-completion available for most every OS. It includes Makefile completion.
(If you're using macOS and Homebrew, you can get this via brew install bash-completion.)
This seems to be default in at least Debian Lenny:
$ grep Makefile /etc/bash_completion
# make reads `GNUmakefile', then `makefile', then `Makefile'
elif [ -f ${makef_dir}/Makefile ]; then
makef=${makef_dir}/Makefile
# before we scan for targets, see if a Makefile name was
# deal with included Makefiles
The header of this file states:
# The latest version of this software can be obtained here:
#
# http://bash-completion.alioth.debian.org/
#
# RELEASE: 20080617.5
Here is a completion script that looks at the .PHONY: declaration.
_make_phony_words() {
local opt_revert
if [ -n "${BASH_VERSION:-}" ]; then
shopt -q nullglob || {
opt_revert=1 ; shopt -s nullglob ;
}
elif [ -n "${ZSH_VERSION:-}" ]; then
[[ -o nullglob ]] || {
opt_revert=1 ; setopt nullglob
}
fi
for f in ./?akefile ./*.make ; do
sed -nEe '/^.PHONY/ { s/^.PHONY:[ ]?// ; p ; } ' "$f" | tr ' ' $'\n' | sort -u
done
if [ -n "$opt_revert" ]; then
[ -n "${ZSH_VERSION:-}" ] && unsetopt nullglob
[ -n "${BASH_VERSION:-}" ] && shopt -u nullglob
fi
unset opt_revert
}
_make_phony_complete() {
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY+=( $(compgen -W "$( _make_phony_words )" -- ${cur}) )
}
complete -F _make_phony_complete make
Makefile completion on steroids!
I had 2 problems with the normal completions:
Problem #1
Sometimes you have targets you want to call like make greet:hi and make greet:hola sort of like namespacing Makefile target names. So your Makefile ends up looking like:
greet\:hola:
echo "hola world"
# OR a .PHONY target
.PHONY: greet\:hi
greet\:hi:
echo "hi world"
In this case the auto-completions after : don't show up as it uses \: in the Makefile as shown above.
Problem #2
There wasn't a way to navigate through the list of all Makefile targets that match my input using arrow keys (or CTRL-p / CTRL-n) in my bash shell.
Basically, I wanted to use fuzzy search like approach on the targets (i.e. fzf).
FZF Repo: https://github.com/junegunn/fzf
Solution
Install FZF Dependency
Using Homebrew
You can use Homebrew (on macOS or Linux)
to install fzf.
brew install fzf
$(brew --prefix)/opt/fzf/install
Using Linux package managers
Package Manager
Linux Distribution
Command
APK
Alpine Linux
sudo apk add fzf
APT
Debian 9+/Ubuntu 19.10+
sudo apt-get install fzf
Conda
conda install -c conda-forge fzf
DNF
Fedora
sudo dnf install fzf
Nix
NixOS, etc.
nix-env -iA nixpkgs.fzf
Pacman
Arch Linux
sudo pacman -S fzf
pkg
FreeBSD
pkg install fzf
pkgin
NetBSD
pkgin install fzf
pkg_add
OpenBSD
pkg_add fzf
XBPS
Void Linux
sudo xbps-install -S fzf
Zypper
openSUSE
sudo zypper install fzf
FZF and : compatible auto-complete command
Put this in your .bashrc
complete -W "\`grep -oE '^[a-zA-Z0-9_.-]+[\\:]*[a-zA-Z0-9_.-]+:([^=]|$)' ?akefile | sort | uniq | sed 's/[^a-zA-Z0-9_.-]*$//' | sed 's/[\]//g' | fzf\`" make
Now just typing make and then hitting the key will work!
DEMO: in action!
Then you can use as following:
make using fzf
I added so I follow "include" directives in Makefile. So my .bashrc looks like this:
function followMakefile() {
grep -oE '^[a-zA-Z0-9_.-]+:([^=]|$)' ?akefile | sed 's/[^a-zA-Z0-9_.-]*$//'
for x in `grep -E '^include' ?akefile | sed 's/include //'`
do
grep -oE '^[a-zA-Z0-9_.-]+:([^=]|$)' $x | sed 's/[^a-zA-Z0-9_.-]*$//'
done
}
complete -W "\`followMakefile\`" make
In Ubuntu 10.04, source the following file:
. /etc/bash_completion
or uncomment it in
/etc/bash.bashrc

Resources