Makefile's targets are available by completion on Linux but, AFAICS, not on Mac OS (10.8.5).
Is it possible to get completion working with this OS?
This seems to achieve simple bash completions for me on El Capitan:
# .bashrc
function _makefile_targets {
local curr_arg;
local targets;
# Find makefile targets available in the current directory
targets=''
if [[ -e "$(pwd)/Makefile" ]]; then
targets=$( \
grep -oE '^[a-zA-Z0-9_-]+:' Makefile \
| sed 's/://' \
| tr '\n' ' ' \
)
fi
# Filter targets based on user input to the bash completion
curr_arg=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -W "${targets[#]}" -- $curr_arg ) );
}
complete -F _makefile_targets make
Here's how this works:
complete -F [function name] [command name] -- this bash builtin register a new completion for [command name] which is generated by the bash function [function name]. So in my code above, if you type make [TAB][TAB] into your shell, you'll trigger the _makefile_targets() function.
if [[ -e "$(pwd)/Makefile" ]]; then -- make sure there's a Makefile in the current directory, otherwise don't try a bash completion.
grep -oE '^[a-zA-Z0-9_-]+:' Makefile -- filter every line of Makefile using the regex for a target name like "test:". -o means only return the part of the line that matches. For example, given a Makefile target like "test: build package", only "test:" will be returned
| sed 's/://' -- taking the grep results, remove the colon from the end of line
| tr '\n' ' ' -- smoosh all targets onto one line, separated by one space
Inside a bash completion function, complete sets several environment variables for you. COMP_WORDS is an array of the list of available bash completion choises based on what the user typed. COMP_CWORD is the index of the currently selected word.
Another very magical builtin compgen will take a list of space separately strings and filter them using the currently selected word. I'm not at all clear how that works.
So, the bottom line is that the last two lines in the function filter our list of makefile targets (stored inside $targets) and shoves them into an array COMPREPLY. The bash completion reads and displays COMPREPLY as choices in the shell.
Inspired by:
https://gist.github.com/tlrobinson/1073865
http://www.thegeekstuff.com/2013/12/bash-completion-complete/ (Esp 9.)
This worked for me on Catalina in the standard zsh terminal
Edit the file named .zshrc in you home directory this can be done with this command in the terminal nano ~/.zshrc
Add the following lines
zstyle ':completion:*:*:make:*' tag-order 'targets'
autoload -U compinit && compinit
exit and save by pressing ctrl + x and then save Y
For macOS Monterey, the following works:
Edit the .zprofile file in your home directory. This can be done with a text editor. Path: ~/.zprofile
Add the following lines to the end of the file:
zstyle ':completion:*:*:make:*' tag-order 'targets'
autoload -Uz compinit && compinit
After that, everything works correctly, tips work when you press TAB.
This method does not display anything extra when entering the terminal, unlike the methods indicated above.
If you use bash and homebrew:
brew install bash-completion
follow their documentation:
Add the following line to your ~/.bash_profile:
[[ -r "$(brew --prefix)/etc/profile.d/bash_completion.sh" ]] && . "$(brew --prefix)/etc/profile.d/bash_completion.sh"
restart your terminal or
source ~/.bash_profile
Related
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.
impossible to find an answer to that:
i would like to create a log history of my command line automatically without having to do anything.
For that i found some clues, i modified my .bash_profile
but i need to exclude some command that i don't want in my log like "ls, cd, etc."
this doesn't work, and i can't d
so here's my code:
# log every command typed and when
command_out=( "more" "less" "cd" "open" "ls" "pwd" "nano" "man" "help") #array of command i don't want to save in my log
my_TEST=0 ##setup a var
FIRST_COMMAND=$(echo $BASH_COMMAND| cut -d' ' -f 1) ##get only the first command
## test if the first command is in the array
for elm in "${command_out[#]}"; do
if [[ $FIRST_COMMAND == $elm ]]; then
echo $elm # does not work
$my_TEST=1 ## if the command is in the array the var is setup to 1
fi
done
if [[ $my_TEST == 0 ]] && [ -n "${BASH_VERSION}" ]; then
trap "caller >/dev/null || \
printf '%s\\n' \"\$(date '+%Y-%m-%dT%H:%M:%S%z')\
\$(tty) \${BASH_COMMAND}\" 2>/dev/null >>~/.command_log" DEBUG
fi
if you any other ideas of how to do what i want i'm open
Thanks you
Bash automatically keeps a history of every command you type; you can use the history command to view it. If you want to exclude certain commands, rather than trying to exclude them from the log, I would skip them when viewing it, e.g. history | egrep -vw 'ls|cd|other|commands|here'.
You can set HISTTIMEFORMAT to get a timestamp with every entry, control how many commands are kept with HISTFILESIZE, and if you really want to keep some commands out instead of just not seeing them when you look, you can list them in HISTIGNORE. See https://www.gnu.org/software/bash/manual/html_node/Using-History-Interactively.html.
Your ~/.bash_history file should already contain your complete command history. You could use something like
cat ~/.bash_history | grep -v cd | egrep -v 'cd|ls|...'
to filter out the commands you're not interested in.
So for the list you specified:
cat ~/.bash_history | egrep -v 'more|less|cd|open|ls|pwd|nano|man|help'
I completed Mark Reed answer with what i precisely wanted. Here is my code:
# changes the .bash_history file mode to append
shopt -s histappend
#configures the history -a command to be run at each shell prompt. The -a immediately writes the current/new lines to the history file.
PROMPT_COMMAND="history -a;$PROMPT_COMMAND"
#list of command i don't want in my history
HISTIGNORE='ls*:exit:pwd:clear:cd*:man*:more*:less*:head*:tail*:nano*:open*:help*'
#set no limit to the history file size
HISTSIZE= HISTFILESIZE=
Bash tab completion adds extra space after the first completion which stops further completion if the compeletion target is a file in multi-level folders.
For example, I have a file in the path ~/Documents/foo/bar.txt, and I want to list it.
I face the following problem, when input
a#b:~$ls Docu <TAB>
I get
a#b:~$ls Documents |(<-this is the cursor, so there is an extra space afer Documents)
So I cannot further tab complete. I have to backspace to delete the extra space.
Normally I want to get:
a#b:~$ls Docu <TAB>
a#b:~$ls Documents/ <TAB>
a#b:~$ls Documents/foo/ <TAB>
a#b:~$ls Documents/foo/bar.txt
Just for the record: There is also a bug in the adobereader-enu (acroread) package that breaks bash completion. In this case you can just delete the symlink:
rm /etc/bash_completion.d/acroread.sh
See also: https://bugs.launchpad.net/ubuntu/+source/acroread/+bug/769866
I have had this same problem with my bash completion in both Ubuntu 11.10 and 12.04. I found that I was able to get many commands to start working correctly by editing /etc/bash_completion. Specifically, I commented out the following section:
####
# makeinfo and texi2dvi are defined elsewhere.
#
#for i in a2ps awk bash bc bison cat colordiff cp csplit \
# curl cut date df diff dir du enscript env expand fmt fold gperf gprof \
# grep grub head indent irb ld ldd less ln ls m4 md5sum mkdir mkfifo mknod \
# mv netstat nl nm objcopy objdump od paste patch pr ptx readelf rm rmdir \
# sed seq sha{,1,224,256,384,512}sum shar sort split strip tac tail tee \
# texindex touch tr uname unexpand uniq units vdir wc wget who; do
# have $i && complete -F _longopt -o default $i
#done
Now ls works well again. I have not figured out yet why mv is still mis-behaving.
This has been answered here at askubuntu. It is related to the bug here
Relevant answer from the above thread:
edit /etc/bash_completion line 1587, change default to filenames (make a backup first).
i also got around the problem by changing
_filedir with _filedir_pdf
in /etc/bash_completion.d/acroread.sh
(Ubuntu 12.04)
acroread bash completion changes the _filedir function thereby altering the behaviour of a lot of other alsobash completion functions
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
How can I find out where an alias is defined on my system? I am referring to the kind of alias that is used within a Terminal session launched from Mac OS X (10.6.3).
For example, if I enter the alias command with no parameters at a Terminal command prompt, I get a list of aliases that I have set, for example:
alias mysql='/usr/local/mysql/bin/mysql'
However, I have searched all over my system using Spotlight and mdfind in various startup files and so far can not find where this alias has been defined. ( I did it a long time ago and didn't write down where I assigned the alias).
For OSX, this 2-step sequence worked well for me, in locating an alias I'd created long ago and couldn't locate in expected place (~/.zshrc).
cweekly:~ $ which la
la: aliased to ls -lAh
cweekly:~$ grep -r ' ls -lAh' ~
/Users/cweekly//.oh-my-zsh/lib/aliases.zsh:alias la='ls -lAh'
Aha! "Hiding" in ~/.oh-my-zsh/lib/aliases.zsh. I had poked around a bit in .oh-my-zsh but had overlooked lib/aliases.zsh.
you can just simply type in alias on the command prompt to see what aliases you have. Otherwise, you can do a find on the most common places where aliases are defined, eg
grep -RHi "alias" /etc /root
First use the following commands
List all functions
functions
List all aliases
alias
If you aren't finding the alias or function consider a more aggressive searching method
Bash version
bash -ixlc : 2>&1 | grep thingToSearchHere
Zsh version
zsh -ixc : 2>&1 | grep thingToSearchHere
Brief Explanation of Options
-i Force shell to be interactive.
-c Take the first argument as a command to execute
-x -- equivalent to --xtrace
-l Make bash act as if invoked as a login shell
Also in future these are the standard bash config files
/etc/profile
~/.bash_profile or ~/.bash_login or ~/.profile
~/.bash_logout
~/.bashrc
More info: http://www.heimhardt.com/htdocs/bashrcs.html
A bit late to the party, but I was having the same problem (trying to find where the "l." command was aliased in RHEL6), and ended up in a place not mentioned in the previous answers. It may not be found in all bash implementations, but if the /etc/profile.d/ directory exists, try grepping there for unexplained aliases. That's where I found:
[user#server ~]$ grep l\\. /etc/profile.d/*
/etc/profile.d/colorls.csh:alias l. 'ls -d .*'
/etc/profile.d/colorls.csh:alias l. 'ls -d .* --color=auto'
/etc/profile.d/colorls.sh: alias l.='ls -d .*' 2>/dev/null
/etc/profile.d/colorls.sh:alias l.='ls -d .* --color=auto' 2>/dev/null
The directory isn't mentioned in the bash manpage, and isn't properly part of where bash searches for profile/startup info, but in the case of RHEL you can see the calling code within /etc/profile:
for i in /etc/profile.d/*.sh ; do
if [ -r "$i" ]; then
if [ "${-#*i}" != "$-" ]; then
. "$i"
else
. "$i" >/dev/null 2>&1
fi
fi
done
Please do check custom installations/addons/plugins you have added, in addition to the .zshrc/.bashrc/.profile etc files
So for me: it was git aliased to 'g'.
$ which g
g: aliased to git
Then I ran the following command to list all aliases
$ alias
I found a whole lot of git related aliases that I knew I had not manually added.
This got me thinking about packages or configurations I had installed. And so went to the
.oh-my-zsh
directory. Here I ran the following command:
$ grep -r 'git' . |grep -i alias
And lo and behold, I found my alias in :
./plugins/git/git.plugin.zsh
I found the answer ( I had been staring at the correct file but missed the obvious ).
The aliases in my case are defined in the file ~/.bash_profile
Somehow this eluded me.
For more complex setups (e.g. when you're using a shell script framework like bash-it, oh-my-zsh or the likes) it's often useful to add 'alias mysql' at key positions in your scripts. This will help you figure out exactly when the alias is added.
e.g.:
echo "before sourcing .bash-it:"
alias mysql
. $HOME/.bash-it/bash-it.sh
echo "after sourcing bash:"
alias mysql
I think that maybe this is similar to what ghostdog74 meant however their command didn't work for me.
I would try something like this:
for i in `find . -type f`; do # find all files in/under current dir
echo "========"
echo $i # print file name
cat $i | grep "alias" # find if it has alias and if it does print the line containing it
done
If you wanted to be really fancy you could even add an if [[ grep -c "alias" ]] then <print file name>
The only reliable way of finding where the alias could have been defined is by analyzing the list of files opened by bash using dtruss.
If
$ csrutil status
System Integrity Protection status: enabled.
you won't be able to open bash and you may need a copy.
$ cp /bin/bash mybash
$ $ codesign --remove-signature mybash
and then use
sudo dtruss -t open ./mybash -ic exit 2>&1 | awk -F'"' '/^open/ {print substr($2, 0, length($2)-2)}'
to list all the files where the alias could have been defined, like
/dev/dtracehelper
/dev/tty
/usr/share/locale/en_CA.UTF-8/LC_MESSAGES/BASH.mo
/usr/share/locale/en_CA.utf8/LC_MESSAGES/BASH.mo
/usr/share/locale/en_CA/LC_MESSAGES/BASH.mo
/usr/share/locale/en.UTF-8/LC_MESSAGES/BASH.mo
/usr/share/locale/en.utf8/LC_MESSAGES/BASH.mo
/usr/share/locale/en/LC_MESSAGES/BASH.mo
/Users/user/.bashrc
/Users/user/.bash_aliases
/Users/user/.bash_history
...
Try: alias | grep name_of_alias
Ex.: alias | grep mysql
or, as already mentioned above
which name_of_alias
In my case, I use Oh My Zsh, so I put aliases definition in ~/.zshrc file.