Why does my bash prompt sometimes get overwritten? - bash

Here's the relevant parts of my bashrc:
function find_git_branch {
local dir=. head
until [ "$dir" -ef / ]; do
if [ -f "$dir/.git/HEAD" ]; then
head=$(< "$dir/.git/HEAD")
if [[ $head == ref:\ refs/heads/* ]]; then
git_branch=" (${head#*/*/})"
elif [[ $head != '' ]]; then
git_branch=' (detached)'
else
git_branch=' (unknown)'
fi
return
fi
dir="../$dir"
done
git_branch=''
}
function shortpath {
# How many characters of the $PWD should be kept
local pwd_length=40
local lpwd="${PWD/#$HOME/~}"
if [ $(echo -n $lpwd | wc -c | tr -d " ") -gt $pwd_length ]
then newPWD="...$(echo -n $lpwd | sed -e "s/.*\(.\{$pwd_length\}\)/\1/")"
else newPWD="$(echo -n $lpwd)"
fi
echo $newPWD
}
PROMPT_COMMAND="find_git_branch; $PROMPT_COMMAND"
# PS1 prompt color vars
CYAN="\e[36m"
RED="\e[31m"
GREEN="\e[32m"
DEFAULT="\e[0m"
TIME="[\t]"
DIRNAME="\w"
export PS1="\u#\h:\[$CYAN\]\$(shortpath)\[$GREEN\]\[\$git_branch\]\[$DEFAULT\] \$ "
It works well, but sometimes as I type or hit the up arrow for previous commands, part of the prompt gets overwritten in the terminal. Why does this happen?

Looks like you're including your $git_branch part in a non-printing-chars block (\[...\]).

Related

bash execute functions based on certain word in arguments

i have the following bash script contains multiple functions
#!/usr/bin/bash
#checks if the arguments is directory and extract the domain name from it
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
example_fun1(){
ping -c 4 $domain
}
example_fun2(){
nslookup $domain
}
for x in "$#" ;do
example_fun1 $x
example_fun2 $x
done
and run as following
./script.sh ./pathtofolder/example.com/ ./pathtofolder/test.com/
Or
./script.sh example.com test.com
and working probably BUT i need to add more feature which is check if certain word based in arguments like fun1 it will execute function example_fun1 only
desired execution
./script.sh fun1 ./pathtofolder/example.com/ ./pathtofolder/test.com/
OR
./script.sh fun1 example.com test.com
Thanks
Try the following
#!/usr/bin/bash
function=""
if [[ $( echo $1 | grep fun1 ) ]]
then
function="example_fun1"
shift
elif [[ $( echo $1 | grep fun2 ) ]]
then
function="example_fun2"
shift
fi
#checks if the arguments is directory and extract the domain name from it
example_fun1(){
ping -c 4 $domain
}
example_fun2(){
nslookup $domain
}
if [[ "$function" != "" ]]
then
for input in "$#"; do
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
"$function"
shift
done
else
for input in "$#"; do
if [[ -d "$1" ]]; then
domain=$(echo "$1" | grep -iEo '[[:alnum:]-]+\.[a-z]+')
WORKING_DIR="$1"
else
domain="$1"
echo "it is domain name"
fi
example_fun1
example_fun2
shift
done
fi
This way you can pass fun1 and execute only fun1
Or if you don't pass any of these for example both of them will be executed
Assign the first parameter to a variable, then use that when calling the function.
func="example_$1"
shift
for x in "$#"; do
"$func" "$x"
done
And your functions need to use their parameters, not a variable that's set in the main script:
example_fun1(){
ping -c 4 "$1"
}
example_fun2(){
nslookup "$1"
}

Bash script freezes when sent SIGINT

I finally grew tired of makefiles and wrote my own bash script to do my compiling. I wrote the whole thing, it works great, but for some reason, it freezes sometimes when I try to cancel it with ctrl-c. Here's the script:
#!/bin/bash
# Just to see if the script even sees the SIGINT
trap caught SIGINT
caught() { echo "hi"; }
compile() {
cpp=$(echo "$1" | sed -e 's/$/.cpp/' -e 's/^/src\//')
o=$(echo "$1" | sed -e 's/$/.o/' -e "s/^/$build_dir\//")
echo "$compile -c $cpp -o $o"
eval "$compile -c $cpp -o $o"
return $?
}
# I know this isn't normal, but I hate it when I forget to include something
# in the header and it fails way down the line
compile_h() {
h=$(echo "$1" | sed -e 's/$/.h/' -e 's/^/src\//')
o=$(echo "$1" | sed -e 's/$/.o/' -e "s/^/$build_dir\/headers\//")
echo "$compile -c $h -o /dev/null"
eval "$compile -x c++ -c $h -o $o"
if [ $? -ne 0 ]; then
return 1
fi
rm "$o"
return 0
}
build_type=$(awk 'NR==1' .build_options)
compile_command_debug=$(awk 'NR==2' .build_options)
link_command_debug=$(awk 'NR==3' .build_options)
compile_command_production=$(awk 'NR==4' .build_options)
link_command_production=$(awk 'NR==5' .build_options)
libraries=$(awk 'NR==6' .build_options)
# Make options for this build
build_dir="build"
compile="$compile_command_debug"
link="$link_command_debug"
if [ "$build_type" == "production" ]; then
build_dir="buildp"
compile="$compile_command_production"
link="$link_command_production"
fi
# These options need to be changeable later
output="game"
job_number=5
# There are more options, but they aren't important for this problem
while [ "$1" != "" ]; do
if [ "$1" == "clean" ]; then
rm -r $build_dir/*
fi
shift
done
# Get filenames
cpps=$(find src -name *.cpp | sed -e 's/src\///' -e 's/.cpp//' | sort)
hs=$(find src -name *.h | sed -e 's/src\///' -e 's/.h//' | sort)
# Ensure that all directories exist
directories=$(find src -type d | tail --lines=+2 | sed 's/src\///' | sort)
if [ ! -d "$build_dir/headers" ]; then
mkdir "$build_dir/headers"
fi
for dir in $directories; do
if [ ! -d "$build_dir/$dir" ]; then
mkdir "$build_dir/$dir"
fi
if [ ! -d "$build_dir/headers/$dir" ]; then
mkdir "$build_dir/headers/$dir"
fi
done
all_o="" # To be used for linking
# Determine what files need to be compiled
cpp_needed=""
h_needed=""
link_needed=false
# Check cpp files
for cpp_base in $cpps; do
o=$(echo "$cpp_base" | sed -e 's/$/.o/' -e "s/^/$build_dir\//")
all_o="$all_o $o"
d_file=$(echo "$cpp_base" | sed -e 's/$/.d/' -e "s/^/$build_dir\//")
if [ -f "$d_file" ]; then
d=$(<"$d_file")
d=$(echo "$d" | tr " " "\n" | tail --lines=+2 | grep "s")
if [ "$link_needed" = false ]; then
if [ "$o" -nt "$output" ]; then
link_needed=true
fi
fi
for dep in $d; do
if [ "$dep" -nt "$o" ]; then
if [ "$cpp_needed" == "" ]; then cpp_needed="$cpp_base"
else cpp_needed="$cpp_needed $cpp_base"
fi
link_needed=true
break
fi
done
else
if [ "$cpp_needed" == "" ]; then cpp_needed="$cpp_base"
else cpp_needed="$cpp_needed $cpp_base"
fi
link_needed=true
fi
done
# Check h files
for h_base in $hs; do
d_file=$(echo "$h_base" | sed -e 's/$/.d/' -e "s/^/$build_dir\/headers\//")
if [ -f "$d_file" ]; then
d=$(<"$d_file")
d=$(echo "$d" | tr " " "\n" | tail --lines=+2 | grep "s")
for dep in $d; do
if [ "$dep" -nt "$d_file" ]; then
if [ "$h_needed" == "" ]; then h_needed="$h_base"
else h_needed="$h_needed $h_base"
fi
break
fi
done
else
if [ "$h_needed" == "" ]; then h_needed="$h_base"
else h_needed="$h_needed $h_base"
fi
fi
done
# Compile
did_something=false
# Compile hs
while [ "$h_needed" != "" ]; do
for index in $(seq 1 $job_number); do
if [ "$h_needed" == "" ]; then break; fi
if ! kill -0 ${pids[index]} 2>/dev/null; then
new_file=$(echo "$h_needed" | awk '{print $1;}')
if [ $(echo "$h_needed" | wc -w) -eq 1 ]; then h_needed=""
else h_needed=$(echo "$h_needed" | cut -d " " -f2-)
fi
compile_h "$new_file" &
pids[index]=$!
did_something=true
fi
done
wait -n
if [ $? -ne 0 ]; then
wait
exit 1
fi
done
while [ $(pgrep -c -P$$) -gt 0 ]; do
wait -n
if [ $? -ne 0 ]; then
wait
exit 1
fi
done
# Compile cpps
while [ "$cpp_needed" != "" ]; do
for index in $(seq 1 $job_number); do
if [ "$cpp_needed" == "" ]; then break; fi
if ! kill -0 ${pids[index]} 2>/dev/null; then
new_file=$(echo "$cpp_needed" | awk '{print $1;}')
if [ $(echo "$cpp_needed" | wc -w) -eq 1 ]; then cpp_needed=""
else cpp_needed=$(echo "$cpp_needed" | cut -d " " -f2-)
fi
compile "$new_file" &
pids[index]=$!
did_something=true
fi
done
wait -n
if [ $? -ne 0 ]; then
wait
exit 1
fi
done
while [ $(pgrep -c -P$$) -gt 0 ]; do
wait -n
if [ $? -ne 0 ]; then
wait
exit 1
fi
done
# Compile program
if [ "$link_needed" = true ]; then
echo "$link $all_o -o game $libraries"
eval "$link $all_o -o game $libraries"
did_something=true
fi
# Make a message if nothing compiled
if [ "$did_something" = false ]; then
echo "Program is already compiled."
fi
It normally works perfectly. However, sometimes, when I try to cancel it with ctrl-c it just freezes. With a bit of debugging I saw that when the script wasn't setting up a new job, ctrl-c would work just fine. But when it was in the middle of setting up a new job, it would freeze the script. It wouldn't even catch the SIGINT (which that "echo hi" thing is for at the top). I honestly have no idea what's going on. Does anybody know what's going on? Thank you!
Edit: I realized I should probably mention I use g++ to compile.
Edit again: Here's an even-more stripped down version of the script. You would still need to setup some files to compile if you wanted to test it:
#!/bin/bash
# Just to see if the script even sees the SIGINT
trap caught SIGINT
caught() { echo "hi"; }
# I know this isn't normal, but I hate it when I forget to include something
# in the header and it fails way down the line
compile_h() {
h=$(echo "$1" | sed -e 's/$/.h/' -e 's/^/src\//')
o=$(echo "$1" | sed -e 's/$/.o/' -e "s/^/$build_dir\/headers\//")
echo "$compile -c $h -o /dev/null"
eval "$compile -x c++ -c $h -o $o"
if [ $? -ne 0 ]; then
return 1
fi
rm "$o"
return 0
}
build_type="debug"
build_dir="build"
compile="g++"
job_number=5
# Get filenames
hs=$(find src -name *.h | sed -e 's/src\///' -e 's/.h//' | sort)
h_needed=$(echo $hs)
# Compile hs
while [ "$h_needed" != "" ]; do
for index in $(seq 1 $job_number); do
if [ "$h_needed" == "" ]; then break; fi
if ! kill -0 ${pids[index]} 2>/dev/null; then
new_file=$(echo "$h_needed" | awk '{print $1;}')
if [ $(echo "$h_needed" | wc -w) -eq 1 ]; then h_needed=""
else h_needed=$(echo "$h_needed" | cut -d " " -f2-)
fi
compile_h "$new_file" &
pids[index]=$!
did_something=true
fi
done
wait -n
if [ $? -ne 0 ]; then
wait
exit 1
fi
done
while [ $(pgrep -c -P$$) -gt 0 ]; do
wait -n
if [ $? -ne 0 ]; then
wait
exit 1
fi
done
Any program that you run in your script may override your trap and set up its own. That trap might for example crash the currently running program for some reason. When this happens, take a look at the process tree in ps wafux to find the most likely culprit. For example, a zombie (Z) or uninterruptible sleep (D) process state (see man ps) is common when a process isn't going anywhere.

How to remove prefix from file names that have x digit numeric prefix

With the help of the community I put the following script together that adds x digit random prefix to all files. The problem is that sometimes I need to add a new file and thats when my script get in trouble
#!/bin/bash
# validate input
[ -n "$1" ] || {
printf "error: insufficient input. Usage: %s /path/to/files\n" "${0//\//}"
exit 1
}
# validate directory
[ -d "$1" ] || {
printf "error: directory not found: '%s'\n" "$1"
exit 1
}
path="$1"
## removing prefix
count=$(ls $path | cut -c1-10 | uniq | wc -l)
echo $count
echo $path
if [ $count = "1" ]
then
cd $path
for i in *; do mv "$i" `echo $i|cut -c11-`; done
echo "PREFIXES REMOVED"
else
echo "PREFIXES ARE NOT UNIQUE, SKIPPING"
fi
mask=$RANDOM$RANDOM$RANDOM; let "mask %= 10000000000";
len=$(echo ${#mask})
## Note: this assumes you are exporting mask earlier. If not, set mask here
## add validation for length 10
if [ $len -lt 10 ]; then
echo "mask is not 10" >&2
exit 1
fi
# move files
prefixcheck=$(ls $path | cut -c1-10 | uniq | wc -l)
if [ $prefixcheck != 1 ]
then
for i in "${path}"/*; do
[ -f "$i" ] || continue # if not file, skip
dir="${i%/*}" # path component
ffname="${i##*/}" # full filename component (with .ext)
mv "$i" "${dir}/${mask}${ffname}"
echo $mask " applied at " $(date) "in " $path >> /home/newprefix
done
else
echo "prefixes exist, skipping"
fi
Let's say I have 20 files with prefixes and i put a new file recording001.mp4 in the folder then my script will fail. How do I tell it to strip prefixes from all files where characters 1-10 in filename is numeric?
To remove the 10-digit prefix from the file names:
cd "$path" &&
for i in [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]*
do
mv "$i" "${i#??????????}"
done

unary operator expected with more than 1 argument

for var in "$#"
do
if test -z $var
then
echo "missing operand"
elif [ -d $var ]
then
echo "This is a directory"
elif [ ! -f $var ]
then
echo "The file does not exist"
else
basename=$(basename $var)
dirname=$(readlink -f $var)
inodeno=$(ls -i $var| cut -d" " -f1)
read -p "remove regular file $#" input
if [ $input = "n" ]
then exit 1
fi
mv $var "$var"_"$inodeno"
echo "$basename"_"$inodeno":"$dirname" >> $HOME/.restore.info
mv "$var"_"$inodeno" $HOME/deleted
fi
done
**Hello, the above code is trying to mimic the rm command in unix. Its purpose is to remove the file .
Eg if I type in bash safe_rm file1 , it works however if type in
bash safe_rm file1 file 2 , it prompts me to remove file 1 twice and gives me a unary operater expected for line 27(if [ $input = "n" ]).
Why does it not work for two files, ideally I would like it to prompt me to remove file1 and file 2.
Thanks
read -p "remove regular file $#" input
should probably be
read -p "remove regular file $var" input
That's the basic.
And this is how I'd prefer to do it:
for T in "$#"; do
if [[ -z $T ]]; then
echo "Target is null."
elif [[ ! -e $T ]]; then
echo "Target does not exist: $T"
elif [[ -d $T ]]; then
echo "Target can't be a directory: $T"
else
BASE=${T##*/}
DIRNAME=$(exec dirname "$T") ## Could be simpler but not sure how you want to use it.
INODE_NUM=$(exec stat -c '%i' "$T")
read -p "Remove regular file $T? "
if [[ $REPLY == [yY] ]]; then
# Just copied. Not sure about its logic.
mv "$T" "${T}_${INODE_NUM}"
echo "${BASE}_${INODE_NUM}:${DIRNAME}" >> "$HOME/.restore.info"
mv "${T}_${INODE_NUM}" "$HOME/deleted"
fi
fi
done

How to pass null string in scripts

How to pass null string using script. as agqmi start 0 "" "" "". if it not able find settings in profile file. And application is not invoking through script. but through command line its working (agqmi start 0 "" "" "").
profile_file
APN='airtelgprs.com'
USR='username'
PASS='password'
PAPCHAP='2'
if [ -f "$PROFILE_FILE" ]; then
echo "Loading profile..." >>$LOG
PAPCHAP=`cat agqmi-network.conf | grep 'PAPCHAP' | awk '{print $1}' | cut -f2
-d"'"`
APN=`cat agqmi-network.conf | grep 'APN' | awk '{print $1}' | cut -f2 -d"'"`
USR=`cat agqmi-network.conf | grep 'USR' | awk '{print $1}' | cut -f2 -d"'"`
PASS=`cat agqmi-network.conf | grep 'PASS' | awk '{print $1}' | cut -f2 -d"'"`
if [ "x$PAPCHAP" == "x" ]; then
PAPCHAP="0"
fi
if [ "x$APN" == "x" ]; then
APN="\"\""
fi
if [ "x$USR" == "x" ]; then
USR="\"\""
fi
if [ "x$PASS" == "x" ]; then
PASS="\"\""
fi
fi
i tried to execute
STATUS_CMD="./agqmi start "$PAPCHAP" "$APN" "$USR" "$PASS""
echo "$STATUS_CMD" >>$LOG
`$STATUS_CMD`
The way to run your command in a way that you store it first is through this (use arrays):
STATUS_CMD=(./agqmi start "$PAPCHAP" "$APN" "$USR" "$PASS")
echo "${STATUS_CMD[*]}" >>$LOG
"${STATUS_CMD[#]}"
You could also use eval but it could misinterpret it depending on the values of your variables.
And you probably no longer need to re-assign your variables that's meant to be empty to "" (literal). Only the one that needs to be converted to 0:
if [ "x$PAPCHAP" == "x" ]; then
PAPCHAP="0"
fi
#if [ "x$APN" == "x" ]; then
# APN="\"\""
#fi
#if [ "x$USR" == "x" ]; then
# USR="\"\""
#fi
#if [ "x$PASS" == "x" ]; then
# PASS="\"\""
#fi
And your comparisons need no markers like x. Using [[ ]] is also recommended.
if [[ $PAPCHAP == '' ]]; then ## Or simply [[ -z $PAPCHAP ]]
PAPCHAP=0
fi
Update for POSIX:
if [ -z "$PAPCHAP" ]; then
PAPCHAP=0
fi
#if [ -z "$APN" ]; then
# APN=''
#fi
#if [ -z "$USR" ]; then
# USR=''
#fi
#if [ -z "$PASS" ]; then
# PASS=''
#fi
STATUS_CMD="./agqmi start \"$PAPCHAP\" \"$APN\" \"$USR\" \"$PASS\""
echo "$STATUS_CMD" >>"$LOG"
./agqmi start "$PAPCHAP" "$APN" "$USR" "$PASS" ## Just execute it directly and not inside a variable.
And maybe you should not add ./?
STATUS_CMD="agqmi start \"$PAPCHAP\" \"$APN\" \"$USR\" \"$PASS\""
echo "$STATUS_CMD" >>"$LOG"
agqmi start "$PAPCHAP" "$APN" "$USR" "$PASS"
You actually don't need to store it on a variable anyway:
echo "agqmi start \"$PAPCHAP\" \"$APN\" \"$USR\" \"$PASS\"" >>"$LOG"
agqmi start "$PAPCHAP" "$APN" "$USR" "$PASS"
Did you try passing "\0" as parameter?
I have a solaris server and whenever I need to pass NULL string as parameter I use "\0".
Your command would look like
agqmi start 0 "\0" "\0" "\0"
Please let me know if it works for you.

Resources