Including/Importing aliases and functions for both bash and zsh - bash

I have a directory of functions and aliases I'd like to include for both bash and zsh terminal invocations (So I don't need to put every function and alias into every separate script and to facilitate organization/tidying of .rc files)
What I've tried so far hasn't worked. Just setting this out for further suggestions.

For zsh I'm using
if [ -d ~/.zsh.d ]; then
for i in ~/.zsh.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
Should work for bash, too.

Related

What environment variable do I need to configure to move my .bash* files to a .bash directory?

I read this page from the bash reference manual, which tells me that bash reads /etc/profile then my home directory. I wasn't able to find an environment variable to tell bash to read some subdirectory, ie ~/.bash.d. I suspect that I need to put some line in /etc/profile along the lines of:
BASHENVIRONMENTVARIABLE="~/.bash.d"
ie. for zsh that variable is ZDOTDIR.
/etc/profile has the following description:
if [ -d /etc/profile.d ]; then
for i in /etc/profile.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
How about writing a script to read files under ~/.bash.d as same as this?
if [ -d ~/.bash.d ]; then
for i in ~/.bash.d/*; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
Or maybe you can do it with eval command.
eval $(cat ~/.bash.d/*)
The startup scripts (~/.bashrc, ~/.bash_profile, ~/.bash_login, etc) are all expected to be located in the home folder.
To source the startup from a different folder, simply add a source (or .) command to the "official", and place all the setting in the desired location (~/.bash.d)
# This is ~/.bashrc
source ~/.bash.d/.bashrc
# This is ~/.bash_login
source ~/.bash.d/.bash_login
Note that the 'BASH_ENV' will usually include absolute path, to allow it to work regardless of the directory where bash in invoked.

Is there a difference between /etc/profile and a bash init file?

I am trying to extend my bash history size from 1000 commands to 10000 commands.
I am trying to follow this tutorial to extend my bash history from 1000 commands to 10000. In the first paragraph, it says to append the following three lines to my 'bash init.'
export HISTCONTROL=erasedups
export HISTSIZE=10000
shopt -s histappend
Google lead me to the bash beginner guide and I can't read it, since Bash isn't my first language. I think the following excerpt answers my question, but I'm not sure.
When invoked interactively with the --login option or when invoked as sh, Bash reads the /etc/profile instructions. These usually set the shell variables PATH, USER, MAIL, HOSTNAME and HISTSIZE.
Questions I have:
Am I reading this right when I assume that /etc/profile is the same as a bash initialize?
How can I test if this worked? /etc/profile currently looks like this:
export HISTSIZE=10000
shopt -s histappend
# System-wide .profile for sh(1)
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
if [ "${BASH-no}" != "no" ]; then
[ -r /etc/bashrc ] && . /etc/bashrc
fi
Update: putting those commands in the bashrc didn't seem to do anything, but following this add timestamps to bash history tutorial, I put the commands in /etc/bashrc . My history now has timestamps. Is it safe to assume that .bash_history now saves 100000 commands as well?
Bash may read several different files. Since these are bash specific options that don't work for sh, you should put them in ~/.bashrc and make sure you have a line source ~/.bashrc in ~/.bash_profile.
You can test it by opening a new terminal and running echo $HISTCONTROL and shopt histappend to see whether they have the expected values ("erasedups" and "on").

Changing color of my directories listed by my directory

I tried changing the color/attributes of directory listings of ls command by editing .dircolors file but it doesn't work.
Before that it is worth mentioning that i have added the below changes in the bash.bashrc file.
# enable color support of ls and also add handy aliases
if [ "$TERM" != "dumb" ]; then
[ -e "$HOME/.dircolors" ] && DIR_COLORS="$HOME/.dircolors"
[ -e "$DIR_COLORS" ] || DIR_COLORS=""
eval "`dircolors -b $DIR_COLORS`"
alias ls='ls --color=auto'
#alias dir='ls --color=auto --format=vertical'
#alias vdir='ls --color=auto --format=long'
fi
Also , added the below code in bashrc for specifying the dircolors file path.
d=.dircolors
test -r $d && eval "$(dircolors $d)"
I just got a vague idea about these things as am just beginning to work with ubuntu.
So can someone please help me find out the reason about why am not able to change colors of ls command?
You seem to be doing some of the tests and evals multiple (unnecessary) times. Here's the standard idiom present in Ubuntu skeleton .bashrc files:
if [ -x /usr/bin/dircolors ]; then
[ -r ~/.dircolors ] && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
fi
This tests whether the dircolors program is available (and executable by you) on your system. If so, then it checks whether the .dircolors file exists (and is readable) in your home directory. If so, it executes the dircolors command using your .dircolors file as input and evaluates the output (which basically just sets the LS_COLORS environment variable). If you don't have a .dircolors file, then it executes dircolors with the default colors (and once again evaluates that output).
When it's done you can check the settings it produced:
echo $LS_COLORS
You can read a bit more about what all these colors mean by running:
dircolors --print-database

Bash: Output all aliases and functions in a script

Take the following script:
shopt -s expand_aliases
set -f
result=$(compgen -A function)
echo $result
When running it outputs all my custom bash functions:
mp3gain pkg-pkgbuild-download quote quote_readline restart standby turnoff turnoff-timer youtubeConvert
However, when slightly changing the script to output aliases, the output is empty:
shopt -s expand_aliases
set -f
result=$(compgen -A alias)
echo $result
Yet it is not empty if I run compgen -A alias directly.
My aliases are stored in ~/.bash_aliases and my functions in /.bash_functions. Both are sources in ~/.bashrc:
# Functions
if [ -f ~/.bash_functions ]; then
. ~/.bash_functions
fi
# Aliases
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
What am I missing here?
I'd be willing to wager that the aliases are not sourced. This can be verified running this simple script:
#!/bin/bash
alias
If there is no output, the aliases are not sourced. Hence, that's why compgen returns an empty list when put in a script (non-sourced aliases) but works fine when run manually in a shell with sourced aliases.
Solution: put "source ~/.bash_aliases" near the top of your script to make sure they are invoked before running.

How to change current working directory inside command_not_found_handle

I'm trying to write a not found handle in Bash that does the following:
If $1 exists and it's a directory, cd into it.
If $1 exists inside a user defined directory $DEV_DIR, `cd into it.
If the previous conditions don't apply, fail.
Right now I have something like this:
export DEV_DIR=/Users/federico/programacion/
function command_not_found_handle () {
if [ -d $1 ]; then # the dir exists in '.'
cd $1
else
to=$DEV_DIR$1
if [ -d $to ]; then
cd $to
echo `pwd`
else
echo "${1}: command not found"
fi
fi
}
And although it seems to be working (the echo pwd command prints the expected dir), the directory in the actual shell does not change.
I was under the impression that since this is a function inside my .bashrc the shell wouldn't fork and I could do the cd but apparently that's not working. Any tips on how to solve this would be appreciated.
I think what's going on is that the shell fork()s after setting up any redirections but before looking for commands, so command_not_found_handle can't affect the interactive shell process.
What you seem to want to do may partly possible using the autocd feature:
shopt -s autocd
From man bash:
autocd - If set, a command name that is the name of a directory
is executed as if it were the argument to the cd com‐
mand. This option is only used by interactive shells.
Otherwise, just create a function that you invoke by name that performs the actions you are trying to use command_not_found_handle for.
It won't change directies if you run this program as a script in your main shell because it creates a sub-shell when it executes. If you source the script in your current shell then it will have the desired effect.
~/wbailey> source command_not_found.sh
That said, I think the following would achieve the same result:
wesbailey#feynman:~/code_katas> cd xxx 2> /dev/null || cd ..; pwd
/Users/wesbailey
just replace the ".." with your env var defined directory and create an alias in your .bashrc file.
I've had the very same wish and the solution that I've been using for a while was opening a new tab in gnome terminal by issuing the command gnome-terminal --tab --working-directory="$FOLDER" from inside the command_not_found handle.
But today I've come up with a solution which is not tied to a specific terminal application, but has exactly the intended behaviour.
The solution uses the PROMPT_COMMAND, which is run before each prompt. The PROMPT_COMMAND is bound to a function responsible for checking for a file related to current shell, and cd'ing into the directory specified in that file.
Then, the command_not_found_handle fills in the file when a change in directory is desired. My original command_not_found_handle also checkout a git branch if the current directory is a git repository and the name matches an existing branch. But to keep focus on answering the current question, I've stripped that part of code.
The command_not_found_handle uses find for searching for the directory matching the given name and goes only 2 levels deep in the directory tree, starting from a configured list.
The code to be added to bash_rc follows:
PROMPT_COMMAND=current_shell_cd
CD_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/bash-cd/$$.cd"
current_shell_cd() {
if [ -r "$CD_FILE" ]; then
local CD_TARGET="$( cat "$CD_FILE" )"
[ ! -z "$CD_TARGET" ] && cd "$CD_TARGET" 2>/dev/null
rm "$CD_FILE"
fi
}
command_not_found_handle () {
local COMMAND="$1";
# List folders which are going to be checked
local BASE_FOLDER_LIST=(
"$HOME/Desenvolvimento"
"/var/www/html"
"$HOME/.local/opt/"
)
local FOLDER=$(
find "${BASE_FOLDER_LIST[#]}" \
-maxdepth 2 -type d \
-iname "$COMMAND" -print -quit )
if [ ! -z "$FOLDER" -a -d "$FOLDER" ]
then
mkdir -p "$( dirname "$CD_FILE" )"
echo "$FOLDER" > "$CD_FILE"
else
printf "%s: command not found\n" "$1" 1>&2
return 127
fi
}

Resources