Why of this strange behavior from my custom bash prompt? - bash

This is my script for my custom prompt (.bashrc):
my_prompt () {
if [[ $? -eq 0 ]]; then # set an error string for the prompt, if applicable
ERRPROMPT="โœ”"
else
ERRPROMPT="โœ˜"
fi
if [[ "\$(type -t __git_ps1)" ]]; then # if we're in a Git repo, show current branch
BRANCH="\$(__git_ps1 '[ %s ] ')"
fi
if [[ -n "$SSH_CLIENT" ]]; then
REMOTE="๐Ÿ’ป${SSH_CLIENT%% *} "
fi
if [[ $EUID -eq 0 ]]; then
COLUSER=9 # root
else
COLUSER=112 # human
fi
local BKG1="\[\033[48;5;${COLUSER}m\]"
local FBKG1="\[\033[38;5;${COLUSER}m\]"
local BKG2="\[\033[48;5;11m\]"
local FBKG2="\[\033[38;5;11m\]"
local BOLD="\[$(tput bold)\]"
local FBLACK="\[\033[38;5;0m\]"
local FLIGHTBLUE="\[\033[38;5;159m\]" #195
local FLIGHTGRAY="\[\033[38;5;244m\]"
local ICON1="${BOLD}${FLIGHTBLUE}"
local ICON2="${BOLD}${FBLACK}${BKG2}"
local RESET="\[$(tput sgr0)\]" # "\[\033[0;39m\]"
local TEXT1="${RESET}${FLIGHTGRAY}"
local TEXT2="${RESET}${FBLACK}${BKG2}"
if [[ -n "$DESKTOP_SESSION" || -n "$REMOTE" ]] ; then
echo "\n${BKG1}${FBLACK}${ERRPROMPT} ๐Ÿ™ฝ\u#\h${RESET}${FBKG1}โ–Ž${REMOTE}${ICON1}๐Ÿ—${TEXT1}\w ${ICON1}๐Ÿ•”${TEXT1}\A\n${ICON2}๐Ÿ—${TEXT2}๐Ÿ™ผ\W${RESET}${FBKG2}โ–Ž${BOLD}โŒ˜${RESET} "
else
echo '[\u#\h \W]\$ '
fi
}
export PS1=`my_prompt`
The desired output is like this:
The problem occurs when navigating history (keys up/down) and sometimes pressing key Home, my cursor is positioned in a place that does not correspond or the text displayed overwrites prompt or the cursor overwrites text displayed. See this picture:
So, where is the problem? and Why of it? What's the solution? Thanks in advance.

Related

What is the best way to accept a 2nd user input from options defined by the 1st user input?

My background is in SQL but I've been learning Bash to create tools to help non-Linux users find what they need from my Linux system - I am pretty green with Bash, I apologize if this looks a bit dumb.
The goal of the script is to essentially display all directories within the current directory to the user, and allow them to input 1-9 to navigate to lower directories.
My sticking point is that I'm trying to use arrays to define potential filepaths, since in practice new directories will be added over time and it is not practical to edit the script each time a filepath is added.
Here's my prototype so far, currently it navigates into Test1, Test2, or Test3 then echos pwd to prove it is there.
#Global Variables
DIR_MAIN='/home/admin/Testhome'
#Potential Filepaths
#/home/admin/Testhome/Test1/Test1-1/
#/home/admin/Testhome/Test1/Test1-2/
#/home/admin/Testhome/Test2/Test2-1/
#/home/admin/Testhome/Test2/Test2-2/
#/home/admin/Testhome/Test3/Test3-1/
#/home/admin/Testhome/Test3/Test3-2/
#Defining Array for first user input
arr=($(ls $DIR_MAIN))
#System to count total number of directories in filepath, then present to user for numbered selection
cnt=0
for i in ${arr[#]}
do
cnt=$(($cnt+1))
echo "$cnt) $i"
done
read -p "Select a folder from the list: " answer
case $answer in
1)
cd $DIR_MAIN/${arr[0]}
echo "Welcome to $(pwd)"
;;
2)
cd $DIR_MAIN/${arr[1]}
echo "Welcome to $(pwd)"
;;
3)
cd $DIR_MAIN/${arr[2]}
echo "Welcome to $(pwd)"
;;
esac
I've tried the following, but it doesn't like the syntax (to someone experienced I'm sure these case statements look like a grenade went off in vim).
I'm beginning to wonder if the SELECT CASE road I'm going down is appropriate, or if there is an entirely better way.
#User Input Start
echo "What is the secret number?"
while :
do
read STRING1
case $STRING1 in
1)
echo "Enter the number matching the directory you want and I will go there"
echo "1 - ${arr[0]}"
echo "2 - ${arr[1]}"
echo "3 - ${arr[2]}"
read STRING2
case $STRING2 in
1)
cd $DIR_MAIN/${arr[0]}
echo "Welcome to" $(pwd)
2)
cd $DIR_MAIN/${arr[1]}
echo "Welcome to" $(pwd)
3)
cd $DIR_MAIN/${arr[2]}
echo "Welcome to" $(pwd)
*)
echo "Thats not an option and you know it"
*)
echo "1 is the secret number, enter 1 or nothing will happen"
;;
esac
#break needs to be down here somewhere
done
Ultimately I know I'll need to variabilize a local array once I'm in Test2 for example (since in practice, this could descend as far as /Test2/Test2-9 and there would be tons of redundant code to account for this manually).
For now, I'm just looking for the best way to present the /Test2-1 and /Test2-2 filepaths to the user and allow them to make that selection after navigating to /Test2/
This might do what you wanted.
#!/usr/bin/env bash
shopt -s nullglob
n=1
for i in /home/admin/Testhome/Test[0-9]*/*; do
printf '%d) %s\n' "$n" "$i"
array[n]="$i"
((n++))
done
(( ${#array[*]} )) || {
printf 'It looks like there is/are no directory listed!\n' >&2
printf 'Please check if the directories in question exists!\n' >&2
return 1
}
dir_pattern_indices=$(IFS='|'; printf '%s' "#(${!array[*]})")
printf '\n'
read -rp "Select a folder from the list: " answer
if [[ -z $answer ]]; then
printf 'Please select a number and try again!' >&2
exit 1
elif [[ $answer != $dir_pattern_indices ]]; then
printf 'Invalid option %s\n' "$answer" >&2
exit 1
fi
for j in "${!array[#]}"; do
if [[ $answer == "$j" ]]; then
cd "${array[j]}" || exit
printf 'Welcome to %s\n' "$(pwd)"
break
fi
done
The script needs to be sourced e.g.
source ./myscript
because of the cd command. See Why can't I change directory using a script.
Using a function instead of a script.
Let's just name the function list_dir
list_dir() {
shopt -s nullglob
declare -a array
local answer dir_pattern_indices i j n
n=1
for i in /home/admin/Testhome/Test[0-9]*/*; do
printf '%d) %s\n' "$n" "$i"
array[n]="$i"
((n++))
done
(( ${#array[*]} )) || {
printf 'It looks like there is/are no directory listed!\n' >&2
printf 'Please check if the directories in question exists!\n' >&2
return 1
}
dir_pattern_indices=$(IFS='|'; printf '%s' "#(${!array[*]})")
printf '\n'
read -rp "Select a folder from the list: " answer
if [[ -z $answer ]]; then
printf 'Please select a number and try again!' >&2
return 1
elif [[ $answer != $dir_pattern_indices ]]; then
printf 'Invalid option %s\n' "$answer" >&2
return 1
fi
for j in "${!array[#]}"; do
if [[ $answer == "$j" ]]; then
cd "${array[j]}" || return
printf 'Welcome to %s\n' "$(pwd)"
break
fi
done
}
All of the array names and variables are declared local to the function in order not to pollute the interactive/enviromental shell variables.
Put that somewhere in your shellrc file, like say in ~/.bashrc then source it again after you have edited that shellrc file.
source ~/.bashrc
Then just call the function name.
list_dir
I took what #Jetchisel wrote and ran with it - I see they updated their code as well.
Between that code and what I hacked together piggybacking off what he wrote, I'm hoping future viewers will have what they need to solve this problem!
My code includes a generic logging function (can write to a log file if you define it and uncomment those logging lines, for a script this size I just use it to output debugging messages), everything below is the sequence used.
As he mentioned the "0" element needs to be removed from the array for this to behave as expected, as a quick hack I ended up assigning array element 0 as null and adding logic to ignore null.
This will also pull pretty much anything in the filepath, not just directories, so more tweaking may be required for future uses but this serves the role I need it for!
Thank you again #Jetchisel !
#hopt -s nullglob
DIR_MAIN='/home/admin/Testhome'
Dir_Cur="$DIR_MAIN"
LOG_LEVEL=1
array=(NULL $(ls $DIR_MAIN))
########FUNCTION LIST#########
####Generic Logging Function
Log_Message()
{
local logLevel=$1
local logMessage=$2
local logDebug=$3
local dt=$(date "+%Y-%m-%d %T")
##Check log level
if [ "$logLevel" == 5 ]
then local logLabel='INFO'
elif [ "$logLevel" == 1 ]
then local logLabel='DEBUG'
elif [ "$logLevel" == 2 ]
then local logLabel='INFO'
elif [ "$logLevel" == 3 ]
then local logLabel='WARN'
elif [ "$logLevel" == 4 ]
then local logLabel='ERROR'
fi
##Check conf log level
if [ "$LOG_LEVEL" == 1 ]
then #echo "$dt [$logLabel] $logMessage" >> $LOG_FILE ##Log Message
echo "$dt [$logLabel] $logMessage" ##Echo Message to Terminal
##Check if Debug Empty
if [ "$logDebug" != "" ]
then #echo "$dt [DEBUG] $logDebug" >> $LOG_FILE ##Extra Debug Info
echo "$dt [DEBUG] $logDebug" ##Extra Debug Info
fi
elif [ "$logLevel" -ge "$LOG_LEVEL" ]
then #echo "$dt [$logLabel] $logMessage" >> "$LOG_FILE" ##Log Message
echo "$dt [$logLabel] $logMessage"
fi
}
####
####Function_One
##Removes 0 position in array by marking it null, generates [1-X] list with further filepaths
Function_One()
{
Log_Message "1" "entered Function_One"
local local_array=("$#")
Log_Message "1" "${local_array[*]}"
n=1
for i in "${local_array[#]}"; do
if [ "$i" != "NULL" ]
then
printf '%d) %s\n' "$n" "$i"
array[n]="$i"
((n++))
fi
done
printf '\n'
read -rp "Select a folder from the list: " answer
for j in "${!local_array[#]}"; do
if [[ $answer == "$j" ]]; then
cd "$Dir_Cur/${local_array[j]}" || exit
printf 'Welcome to %s\n' "$(pwd)"
break
fi
done
}
####
########FUNCTION LIST END#########
########MAIN SEQUENCE########
echo "Script start"
Function_One "${array[#]}"
Dir_Cur="$(pwd)"
array2=(NULL $(ls $Dir_Cur))
Function_One "${array2[#]}"
Dir_Cur="$(pwd)"
$Dir_Cur/test_success.sh
echo "Script end"
########

Issue in echo statement in shell scripting

I have a very peculiar issue with a script that I have wrote today. I am trying to form an ip address from two variables namely url and port. I am getting the url value from a library script which echos 10.241.1.8 and the port number is 10000. Now if I concatenate both the url and the port into another variable ip, I get completely a strange result(:10000241.1.8). I have my code and its result below. Please help me with your suggestions to fix this.
clear
echo $(date +'%H:%M:%S')'>> "Sample Records" Script started...'
usage() {
echo ">> $ script.sh -ctoff 89 -env c -ns reporting -depPath /user/release/audit_prime_oozie"
echo "Usage: $ script.sh -ctoff <Cutoff number> -env <testing cluster. ex: s for staging,c,d,p and a> -ns <optional: hive namespace> -depPath <deployment path>"
}
# Function to validate if value of a parameter is not empty
validate () {
if [[ $flag != 1 ]]; then
if [[ $tmpVar == *"-"* ]] || [[ -z $tmpVar ]]; then
usage
exit 1
fi
fi
}
options=$#
if [[ -z $options ]]; then
usage
exit 1
fi
arguments=($options)
index=0
# Function to extract the parameter values
check (){
for x in $options
do
index=`expr $index + 1`
case $x in
-ctoff)
cutOff="${arguments[index]}"
tmpVar=$cutOff
validate $tmpVar
;;
-env)
env="${arguments[index]}"
tmpVar=$env
validate $tmpVar
;;
-ns)
ns="${arguments[index]}"
tmpVar=$ns
validate $tmpVar
;;
-depPath)
depPath="${arguments[index]}"
tmpVar=$depPath
validate $tmpVar
;;
esac
if [[ -z $ns ]];then
ns=reporting
fi
done
}
check $#
error_exit(){
echo "$1" 1>&2
exit 1
}
# Create the execution directory
user=$(id -u -n)
PWD=`pwd`
INSTALL_ROOT=$PWD
LOCAL_DIR="/tmp/$user/sample_duns"
if [[ ! -d $LOCAL_DIR ]]; then
mkdir -p $LOCAL_DIR
echo ">> Created local directory $LOCAL_DIR"
if [[ $? -ne 0 ]]; then
echo ">> Unable to create $LOCAL_DIR, writing to current folder $INSTALL_ROOT"
LOCAL_DIR=$INSTALL_ROOT
fi
fi
if [[ $(ls -A $LOCAL_DIR) ]]; then
echo ">> Removed the temp files from $LOCAL_DIR"
rm -r $LOCAL_DIR/*
fi
# create the file name
datestamp=$(date '+%Y%m%d%H')
outFile=sample_duns_$datestamp.txt
# Copy the contents from HDFS to Local directory
echo ">> Copying required files from HDFS"
hdfs dfs -copyToLocal $depPath/data-warehouse/config/server.properties $LOCAL_DIR || error_exit "Cannot copy files from HDFS! Exiting now.."
hdfs dfs -copyToLocal $depPath/data-warehouse/reporting/lib_getHiveServer2ip.sh $LOCAL_DIR || error_exit "Cannot copy files from HDFS! Exiting now.."
if [[ $? -ne 0 ]]; then
echo ">> Files missing. Exiting now.."
exit 1
fi
# Call the lib script to get appropriate hiveserver2 ip address from the supplied environment for beeline execution
echo ">> Reading the HiveServer2 ip"
chmod +x $LOCAL_DIR/lib_getHiveServer2ip.sh
url=$($LOCAL_DIR/lib_getHiveServer2ip.sh $env $LOCAL_DIR/server.properties)
echo url=$url
port=10000
echo ip=$url:$b
Here is my output from the terminal.
11:18:16>> "Sample Records" Script started...
>> Removed the temp files from /tmp/user/sample_duns
>> Copying required files from HDFS
>> Reading the HiveServer2 ip
url=10.241.1.8
:10000241.1.8
I am expecting the below result
ip=10.241.1.8:10000
Adding the lib_getHiveServer2ip.sh script below
. $2 # read properties file
if [[ $1 == "d" ]]; then
ip=$devHSer
elif [[ $1 == "c" ]]; then
ip=$crankHSer
elif [[ $1 == "s" ]]; then
ip=$stgHSer
elif [[ $1 == "p" ]]; then
ip=$prdHSer
elif [[ $1 == "a" ]]; then
ip=$alpHSer
else
echo ">> Invalid cluster ip encountered. Exiting now ..."
exit 1
fi
echo $ip
Your url variable contains a carriage return character for some reason. Check lib_getHiveServer2ip.sh for weirdness.
Pipe your echo output to hexdump to confirm.
Edit: looks like your properties file has bad line endings. Use the file utility to check.

Mac OSX | Bash .bash_profile not updating PS1

I have a small script named .bash_prompt which is called by source ~/.bash_prompt in ~/.bash_profile.
The script sets my PS1 to display some useful information about the current git repo.
Unfortunately the git-part is only being executed when spawning a new terminal, so the branch is only displayed when I call the script manually after changing to a git repo.
How can I make my bash prompt update everytime I execute a command?
function git_branch() {
local GITDIR=$(git rev-parse --show-toplevel 2>&1)
if [[ "$GITDIR" != '/Users/\u' ]]
then
local BRANCH=`git branch 2> /dev/null | sed -n '/^\*/s/^\* //p'`
if [ -n "$BRANCH" ]; then
echo -e "$BRANCH"
fi
else
echo ""
fi
}
function git_prompt() {
local prompt_unpushed_symbol="โ–ณ"
local prompt_unpulled_symbol="โ–ฝ"
local prompt_dirty_symbol="*"
local prompt_synced_symbol="โœ“"
local local_branch=$(git_branch)
local remote_branch="origin/$local_branch"
local first_log="$(git log $local_branch $remote_branch -1 2> /dev/null)"
local STATUS=`git status 2>&1`
if [[ "$STATUS" == *'Not a git repository'* ]]; then
echo ""
elif [[ "$STATUS" != *'working directory clean'* ]]; then
echo "[$local_branch $prompt_dirty_symbol]"
elif [[ "$STATUS" == *'Your branch is ahead'* ]]; then
echo "[$local_branch $prompt_unpushed_symbol]"
elif [[ -n "$first_log" ]]; then
echo "[$local_branch $prompt_unpulled_symbol]"
else
echo "[$local_branch $prompt_synced_symbol]"
fi
}
function colorPrompt {
local c_brace="\[\033[m\]"
local c_git="\[\033[31m\]"
local user_host="\[\033[36m\]\u\[\033[m\]#\[\033[32m\]\h"
local location="\[\033[33;1m\]\w"
local tail="\n\$ "
export PS1="[$user_host $location$c_brace]$c_git$(git_prompt)$c_brace$tail"
}
colorPrompt
The value of the PROMPT_COMMAND shell variable is executed prior to displaying the prompt; one of the main uses of this feature is to set the value of PS1. In your case, all you need to do is add
PROMPT_COMMAND=color_prompt
to your .bash_profile after sourcing .bash_prompt.

Bash if block doesn't run when it clearly should

The code:
first=true
mountpoint=" "
partlist=`df -h | grep "^/dev"` # get partition info
for i in $partlist # loop through info
do
if [[ ${i:0:1} = "/" ]] # items starting with / are what I'm interested in
then
if [[ $first ]] # The first instance in each pair is the device name
then
mountdev=$i
mountpoint=" "
first=false
else # The second instance is the device mount point
mountpoint=$i
first=true
fi
if [[ $mountpoint != " " ]] # If the mountpoint was just set (last to be set
# before printing config)
then
# Print config
echo "${mountpoint} \${fs_size ${mountpoint}"
echo "USED \${fs_used ${mountpoint}}\${alignr}\${fs_free ${mountpoint}} FREE"
echo "\${fs_bar 3,300 ${mountpoint}}"
echo "READ \${diskio_read ${mountdev}}\${alignr}\${diskio_write ${mountdev}} WRITE"
echo ""
fi
fi
done
The problem:
The goal is to create a script that will generate my preferred conky config for each of my computers without having to edit the file for each computer. The above is a snippet that generates the section which reports on my disk usage information. Unfortunately I'm having trouble having it output ANYTHING. I throw various debug echos into it and it seems to all be working right except the if statement that actually output the config. I can't figure out what's wrong with it though. Any suggestions or assistance?
Advance thank you :)
This is the wrong line:
if [[ $first ]] # The first instance in each pair is the device name
That would always evaluate to true. [[ true ]] or [[ false ]] is synonymous to [[ -n true ]] or [[ -n false ]] which is always correct.
What you probably meant was
if "$first"
Or
if [[ $first == true ]]

Need help on ssh usage in a loop of shell script

I need help on how to connect remote systems through ssh command in while loop of shell script. I was able to connect one remote system using ssh from shell script. Please find sample code snippet as given belowโ€ฆ..
ssh "root#148.147.179.100" ARG1=$rpmFileName 'bash -s' <<'ENDSSH'
echo ">>Checksum ..."
md5sum /root/$ARG1
ENDSSH
When tried to run same piece of code within a loop getting the error "syntax error: unexpected end of file", which I couldnโ€™t resolve.
But when placing the same piece of code in another script file and using that file in while loop of another script, is working.
Can anyone help me with some solution.
Please find entire code as given below...
#!/bin/sh
rpmFileName=""
file="serverIps.txt"
dir="/home/rtulluri/downloads/EVAT-1123/AxisTar";
numberOfIps=0
axisTarfileTarget='/var'
#This function checks whether given ip is valid or not
#returns 0 for valid ip, 1 for invalid ip
function valid_ip()
{
local ip=$1
local stat=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
#echo "stat = $stat"
return $stat
}
#Check whether given file exists or not
if [ -s $file ]
then
echo "$file exists"
else
echo "$file doesn't exist"
echo "exiting ........"
exit
fi
IFS=,
echo "---------------"
while read sysType serverIp uid pwd
do
sysType="${sysType#"${sysType%%[![:space:]]*}"}" # remove leading whitespace characters
sysType="${sysType%"${sysType##*[![:space:]]}"}" # remove trailing whitespace characters
serverIp="${serverIp#"${serverIp%%[![:space:]]*}"}" # remove leading whitespace characters
serverIp="${serverIp%"${serverIp##*[![:space:]]}"}" # remove trailing whitespace characters
uid="${uid#"${uid%%[![:space:]]*}"}" # remove leading whitespace characters
uid="${uid%"${uid##*[![:space:]]}"}" # remove trailing whitespace characters
pwd="${pwd#"${pwd%%[![:space:]]*}"}" # remove leading whitespace characters
pwd="${pwd%"${pwd##*[![:space:]]}"}" # remove trailing whitespace characters
if [ -n "$serverIp" ]
then
valid_ip $serverIp
#Assign the return value to a variable
isValidIp=$?
else
isValidIp=1
fi
if [ $isValidIp -eq "0" ]
then
numberOfIps=$(( $numberOfIps + 1 ))
echo "$numberOfIps) $serverIp --> is valid"
if [ "$sysType" = "ebox" ]
then
echo "$serverIp is an eBox device.."
echo "About to pass $serverIp as argument to connct.sh"
#./connct.sh $serverIp
ssh "$address" ARG1=$rpmFileName 'bash -s' <<'ENDSSH'
echo ">>Checksum ..."
ENDSSH
fi
else
echo "$serverIp --> is invalid"
fi
echo ""
done < $file
You might try to use the '-n' ssh switch
e.g.:
for i in {1..7}
do
ssh -n myhost$i mycommand
done

Resources