Underlining to bash prompt only when pwd changed - bash

I want to underline to my bash shell prompt(=PS1) only when a current directory is changed.
I tried this.
At .bashrc file I wrote
DIR_CHANGED=
function cd {
builtin cd "$#"
DIR_CHANGED=1
}
function dir_ul {
# if $DIR_CHANGED is 1, draw underline
if [ x == x$DIR_CHANGED ]; then echo -en '\033[0;34m'; else echo -en '\033[4;34m'; fi
export DIR_CHANGED=''
}
export PS1='$(dir_ul)\w$(tput sgr0)$ '
But not worked.
How do I fix?

I hate to say it, but I'd call that a bash bug. Here's a workaround: use PROMPT_COMMAND to
copy $DIR_CHANGED and reset it, and in dir_ul refer to that saved copy. Minimal changes:
function dir_ul {
if [ x == x$DIR2 ]; then echo -en '\033[0;34m'; else echo -en '\033[4;34m'; fi
}
PROMPT_COMMAND='DIR2=$DIR_CHANGED;DIR_CHANGED='

Test this in your ~/.bashrc with a second shell:
PREV="$PWD"
PROMPT_COMMAND='[[ $PREV != $PWD ]] && PS1="$(tput smul)\w$(tput rmul)$ " && PREV="$PWD" || PS1="$(tput rmul)\w$ "'

The $(dir_ul) gets evaluated at the instance you set the PS1 variable, rather than being continuously updated.

Related

How to change the terminal title to currently running process?

I know how to change the Terminal Window title. What I am trying to find out is how to make bash not zsh write out the currently running process so if I say do
$ ls -lF
I would get something like this for the title
/home/me/curerntFolder (ls -lF)
Getting the last executed command would be too late since the command has executed already, so it won't set the title with the command that was executed.
In addition to #markp-fuso's answer, here's how I did it to make it work with Starship.
function set_win_title() {
local cmd=" ($#)"
if [[ "$cmd" == " (starship_precmd)" || "$cmd" == " ()" ]]
then
cmd=""
fi
if [[ $PWD == $HOME ]]
then
if [[ $SSH_TTY ]]
then
echo -ne "\033]0; 🏛ī¸ # $HOSTNAME ~$cmd\a" < /dev/null
else
echo -ne "\033]0; 🏠 ~$cmd\a" < /dev/null
fi
else
BASEPWD=$(basename "$PWD")
if [[ $SSH_TTY ]]
then
echo -ne "\033]0; 🌩ī¸ $BASEPWD # $HOSTNAME $cmd\a" < /dev/null
else
echo -ne "\033]0; 📁 $BASEPWD $cmd\a" < /dev/null
fi
fi
}
starship_precmd_user_func="set_win_title"
eval "$(starship init bash)"
trap "$(trap -p DEBUG | awk -F"'" '{print $2}');set_win_title \${BASH_COMMAND}" DEBUG
Note this differs from the Custom pre-prompt and pre-execution Commands in Bash instructions in that the trap is set after starship init. Which I have noted in a bug.
UPDATE: my previous answer (below) displays the previous command in the title bar.
Ignoring everything from my previous answer and starting from scratch:
trap 'echo -ne "\033]0;${PWD}: (${BASH_COMMAND})\007"' DEBUG
Running the following at the command prompt:
$ sleep 10
The window title bar changes to /my/current/directory: (sleep 10) while the sleep 10 is running.
Running either of these:
$ sleep 1; sleep 2; sleep 3
$ { sleep 1; sleep2; sleep 3; }
The title bar changes as each sleep command is invoked.
Running this:
$ ( sleep 1; sleep 2; sleep 3 )
The title bar does not change (the trap does not apply within a subprocess call).
One last one:
$ echo $(sleep 3; echo abc)
The title bar displays (echo $sleep 3; echo abc)).
previous answer
Adding to this answer:
store_command() {
declare -g last_command current_command
last_command=$current_command
current_command=$BASH_COMMAND
return 0
}
trap store_command DEBUG
PROMPT_COMMAND='echo -ne "\033]0;${PWD}: (${last_command})\007"'
Additional reading materials re: trap / DEBUG:
bash guide on traps
SO Q&A
You can combine setting the window title with setting the prompt.
Here's an example using bashs PROMPT_COMMAND:
tputps () {
echo -n '\['
tput "$#"
echo -n '\]'
}
prompt_builder () {
# Window title - operating system command (OSC) ESC + ]
echo -ne '\033]0;'"${USER}#${HOSTNAME}:$(dirs)"'\a' >&2
# username, green
tputps setaf 2
echo -n '\u'
# directory, orange
tputps setaf 208
echo -n ' \w'
tputps sgr0 0
}
prompt_cmd () {
PS1="$(prompt_builder) \$ "
}
export PROMPT_COMMAND=prompt_cmd
For Linux OS Adding following function in bashrc file
Following Steps
Open bashrc file
vi ~/.bashrc
Write a function in bashrc file
function set-title() {
if [[ -z "$ORIG" ]]; then
ORIG=$PS1
fi
TITLE="\[\e]2;$*\a\]"
PS1=${ORIG}${TITLE}
}
save the file
source ~/.bashrc
call function
set-title "tab1"
The easiest way to change the title of the terminal I could think of is to use echo in shell script
echo "\033]0;Your title \007"
And to change open a new tab with new title name is
meta-terminal--tab-t"Your title"

How can I make the colors of the command prompt dynamic?

My attempt to change the colors of the prompt depending on the state of an environment variable:
tw_prompt_color () {
if [[ ! -z $TASKRC ]]; then
echo '\033[34m\'
else
echo '\033[32m\'
fi
}
export -f tw_prompt_color
PS1="\[$(tw_prompt_color)]iMac5K# \[\033[33;1m\]\w:\[\033[m\]\[\033[33m\]\$(parse_git_branch)\[\033[00m\] "
This doesn't work. The prompt remains the same color regardless of the state of $TASKRC unless I manually run exec bash. But oddly, adding exec bash into the tw_prompt_color function doesn't help.
This works for me. Maybe use this approach as a starting point to create your own solution.
1. Edit your ~/.bashrc
fancy_git_prompt(){
git_info=`git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
if [ "${git_info}" ]; then
if [ "$(git status -s)" ]; then
git_color='\033[1;31m'
else
git_color='\033[1;36m'
fi
echo -e "${git_color}git:${git_info}"
fi
}
PS1='\n\[\033[1;32m\][\w] $(fancy_git_prompt)\[\033[0m\]\n\$ '
2. Reload ~/.bashrc
source ~/.bashrc
There are more advanced versions out there, e.g. git-prompt.sh
OK, this works:
tw_prompt_color () {
if [[ ! -z $TASKRC ]]; then
echo -e '\033[34m'
else
echo -e '\033[32m'
fi
}
export -f tw_prompt_color
PS1='$(tw_prompt_color)iMac5K# \[\033[33;1m\]\w:\[\033[m\]\[\033[33m\]$(parse_git_branch)\[\033[00m\] '
Fix #1: Change to single quotes as suggested. I still don't understand why this worked when the $(parse_get_branch) bit worked fine with double quotes.
Fix #2: Add -e to the echo command.
Fix #3: Remove some of the now extraneous brackets and escaping. Totally don't get why I need to do that now but it works.
If anyone who can shed light on my bash cluelessness, I'd appreciate it.

How to return from sourced bash script automatically on any error?

I have a bash script which is only meant to used be when sourced.
I want to return from it automatically on any error, similar to what set -e does.
However setting set -e doesn't work for me because it will also exit the users shell.
Right now I'm handling returning manually like this command || return 1, for each command.
You can also use command || true or command || return.
If your requirement is something different, please update more precisely.
You can use trap. E.g.:
// foo.sh
function func() {
trap 'if [ $? -ne 0 ]; then echo "Trapped!"; return ; fi' DEBUG
echo 'foo'
find -name "foo" . 2> /dev/null
echo 'bar'
}
func
Two notes. First, the trap needs to be inside the function as shown. It won't work if it's just inside the script.
Two, there is a significant limitation. Even if you set the return to the trap (e.g., return 1), while func exists after the bad find command, $? is still zero, no matter what. I'm not sure if there's a way around that, so if it's important to preserve the exit value of the failed command, this may not work.
E.g., if you had:
func
func_return=$?
echo "return value is: $func_return"
func_return will always be zero. I've played around with trying to get the exit value of the failed command to pass out of the function trap and into the function exit value, but have not found a way to do it.
If you need to preserve the return value, you could update a global variable inside the debug trap.
If I understand well, you can set -e locally in each function.
cat sourced
f1 () {
local -
set -e
[ "$1" -eq "$1" ] 2> /dev/null && echo "$1"
}
cat script.sh
. sourced
param='bad'
ret=$(f1 "$param")
[ $? -eq 0 ] && echo "result = $ret" || \
echo "error in sourced file with param $param"
param=3
ret=$(f1 "$param")
[ $? -eq 0 ] && echo "result = $ret" || \
echo "error in sourced file with param $param"

CD into directory via script executed by .bashrc function

I am trying to write a program which will let me cd into a directory. My program is called mangy, I also have a mangy.sh script and a mangy function defined in .bashrc
mangy.sh is as follows
out= $($HOME/.mangy/mangy $#)
if [ "$1" == "g" ] || [ "$1" == "go" ] ; then
cd "$out"
else
echo "$out"
fi
and here is my function in .bashrc
function mangy() {
source $HOME/.mangy/mangy.sh $#
}
export -f mangy
~/Desktop is an example of whatout would contain
When I run the mangy function it outputs /home/ryan/Desktop: Is a directory
but doesn't navigate me there. I swear I had this working at one point but can't remember how I did it
You are trying to source the parameter. Sourcing a script is different from executing it. Sourcing just includes the file, so this should do:
function mangy() {
source $HOME/.mangy/mangy.sh
}
Also, you should quote the $# if you don't want it to be split by spaces.
out=$($HOME/.mangy/mangy "$#")
Also, it may be simpler not to have mangy.sh at all, though it's really your decision. You can put it directly in .bashrc.
function mangy() {
out= $($HOME/.mangy/mangy $#)
if [ "$1" == "g" ] || [ "$1" == "go" ] ; then
cd "$out"
else
echo "$out"
fi
}

SHELL general function for action state

How to make a code bellow as a general function to be used entire script in bash:
if [[ $? = 0 ]]; then
echo "success " >> $log
else echo "failed" >> $log
fi
You might write a wrapper for command execution:
function exec_cmd {
$#
if [[ $? = 0 ]]; then
echo "success " >> $log
else
echo "failed" >> $log
fi
}
And then execute commands in your script using the function:
exec_cmd command1 arg1 arg2 ...
exec_cmd command2 arg1 arg2 ...
...
If you don't want to wrap the original calls you could use an explicit call, like the following
function check_success {
if [[ $? = 0 ]]; then
echo "success " >> $log
else echo "failed" >> $log
fi
}
ls && check_success
ls non-existant
check_success
There's no really clean way to do that. This is clean and might be good enough?
PS4='($?)[$LINENO]'
exec 2>>"$log"
That will show every command run in the log, and each entry will start with the exit code of the previous command...
You could put this in .bashrc and call it whenever
function log_status { [ $? == 0 ] && echo success>>/tmp/status || echo fail>>/tmp/status }
If you want it after every command you could make the prompt write to the log (note the original PS1 value is appended).
export PS1="\$([ \$? == 0 ] && echo success>>/tmp/status || echo fail>>/tmp/status)$PS1"
(I'm not experienced with this, perhaps PROMPT_COMMAND is a more appropriate place to put it)
Or even get more fancy and see the result with colours.
I guess you could also play with getting the last executed command:
How do I get "previous executed command" in a bash script?
Get name of last run program in Bash
BASH: echoing the last command run

Resources