Bash script can't figure out how to concatenating white spaces - bash

I want to center a word in X spaces so I wrote this function:
function center {
str=$1
M=$2
N=${#str}
if (( N >= M )); then
echo $str
exit
fi
P=$((M-N))
HP=$((P/2))
left=""
right=""
for i in [1..HP]; do :
left=" $left"
right=" $right"
done
if (( (P-2*HP) == 1 )); then
right=" "$right""
fi
echo "HP is $HP"
echo "Right is |"${right}"|"
echo "Left is |$left|"
echo "str is |$str|"
res=$right$str$left
echo "$res"
}
Problem is not matter what I do can't get right or left to hold on to more than one whitespace. I have tried the suggestions on other answers but I can't seem to make them work. Please help.

Try this (after some variable quoting and some other fixes):
function center {
str=$1
M=$2
N=${#str}
if (( N >= M )); then
echo "$str"
exit
fi
(( P=M-N ))
(( HP=P/2 ))
left=$(printf '%*s' "$HP" "")
right=$left
(( (P-2*HP) == 1 )) && right=" "$right""
echo "HP is $HP"
echo "Right is |${right}|"
echo "Left is |$left|"
echo "str is |$str|"
res="$right$str$left"
echo "$res"
}
The for i in [1..HP] can not work.
A printf '%*s' "$HP" "" is a more idiomatic way to get $HP spaces.
Is better to build one variable $left and copy it to $right than to build the two.

Related

Error getting in the second if statement in bash script

n=20
x=3
count=0
flag=0
i=1
declare -a arr[n+1]
for (( j=0;j<=n;j++ ))
do
arr+=(0)
done
#echo "${arr[#]}"
while [[ $count -ne $n ]]
do
if [[ $i -le $n ]]
then
if [[ ${arr[$i]} -eq '0' ]]
then
echo "Value is ${arr[$i]}"
#${arr[$(i-1)]}= (( ${arr[$i-1]++} ))
${arr[$i]}+=${arr[$i]}
echo " "
#echo -n "${arr[$i]}"
echo -n " $i"
count=$(( count+1 ))
i=$(( i+1+x ))
else
i=$(( i+1 ))
fi
else
i=$(( i-n ))
flag=$(( flag+1 ))
fi
done
echo " "
echo "No of round : $flag"
This is the whole code, I've tried to print numbers that follows this: n=20 is the number of elements and x=3 is the number that we have to avoid. For example,
20
3
1,5,9,13,17,2,6,10,14,18,3,7,11,15,19,4,8,12,16,20,
3
But, the problem is that my second if condition is not fulfilling, if ignores the condition. Above example is for the C++, but in bash script, 2nd if statement isn't working. This can be because syntax is wrong. So can you please help me to find the mistakes.
Output of the above code:
output
${arr[$i]}+=${arr[$i]}
This is incorrect. $ should not be used when you assign the value.
If you want to double the value, replace this string with the following:
arr[$i]=$(( ${arr[$i]} + ${arr[$i]} ))
Or what you want to do there?

Problem in shell code to find happy prime number

This is my code to find happy prime number, I don't know why it is giving error.
#!/bin/bash
happy(){
rem = $0
sum = $0
while [ $result -gt 0 ];
do
rem = $((result%10))
p = $((rem*rem))
sum = $((sum + p))
result = $((result/10))
done
return $sum
}
echo "Enter a number"
read num
for (( i=1; i<101; i++ ))
do
result=$i
while [ $result -ne 0 && $result -ne 4 ];
do
happy $result
done
if [ $?==1 ]
then echo "happy number"
else
echo "not a happy number"
fi
done
I see lots of syntax errors and some logical errors in your script.
This should be a fixed version (works at least for 13 and 4 :))
Errors I've found:
rem = $0 and similar: the spaces around assignments are not allowed in bash,
rem = $0: you assign a value that is never used,
rem = $0 and sum = $0 the first argument passed to the function is $1 not $0,
the input number is assigned to num but never used,
the exit condition from the for and while loops is broken,
...
#!/bin/bash
happy()
{
result=$1
sum=0
while [ $result -gt 0 ]; do
rem=$(( result % 10 ))
p=$(( rem * rem ))
sum=$(( sum + $p ))
result=$(( result / 10 ))
done
echo "$sum"
}
echo "Enter a number"
read num
result=$num
for (( i=1; i<101; i++ )) do
result=$(happy $result)
if [ $result == 1 ]; then
echo "$num is a happy number"
exit
fi
done
echo "$num is not a happy number"

Quicksort implemented in shell script doesn't work

I am learning shell scripting and trying to implement quick sort using it.
But it doesn't work, actually it acting weird.
The script:
#!/bin/bash
declare -a data=()
declare -r size=10
declare -i steps=0
for i in $(seq 0 $size); do
data[$i]=$(expr $RANDOM % $size)
done
function partition() {
pivot=${data[$1]}
left=$(expr $1 + 1)
right=$2
while true; do
while [[ $left -le $right && ${data[$left]} -le $pivot ]]; do
left=$(expr $left + 1)
steps=$(expr $steps + 1)
done
while [[ $right -ge $left && ${data[$right]} -ge $pivot ]]; do
right=$(expr $right - 1)
steps=$(expr $steps + 1)
done
if [[ $left -gt $right ]]; then
break
fi
temp=${data[$left]}
data[$left]=${data[$right]}
data[$right]=$temp
done
temp=${data[$1]}
data[$1]=${data[$right]}
data[$right]=$temp
echo $right
}
function quickSort() {
if [[ $1 -lt $2 ]]; then
local partitionPoint=$(partition $1 $2)
quickSort $1 $(expr $partitionPoint - 1)
quickSort $(expr $partitionPoint + 1) $2
fi
}
# involve the algorithm
quickSort 0 $(expr $size - 1)
echo "Steps: $steps"
echo ${data[#]}
I tried to log some variable but it's just weird I can't figure out what's going on.
When I comment out all the code in the two functions and 'manually' update elements of data variable, it did changed.
I tried to log some variables and they all changing.
But the final output remains untouched.
Or maybe it eventually reversed all the flipping but I don't know.
I can't figure it out.
At last I compare my python implementation line by line. No mistakes. But it just not working.
Am I miss something?
Variable scope or something?
Any advice will be appreciated.
There are several smaller issues in this code, but the biggest issue is here:
partitionPoint=$(partition $1 $2)
This is problematic because $( ... ) runs ... in a subshell -- a separate, fork()ed-off process, consequently with its own variable scope.
If you instead return your result via indirect assignment, making it:
partition "$1" "$2" partitionPoint
and inside the function using:
printf -v "$3" %s "$right"
...to assign the value to the variable so named, things work much better.
#!/bin/bash
PS4=':$LINENO+'; set -x
data=()
size=10
steps=0
for ((i=0; i<size; i++)); do
data[$i]=$((RANDOM % size))
done
partition() {
local pivot left right dest temp
pivot=${data[$1]}
left=$(($1 + 1))
right=$2
dest=$3
while true; do
while (( left <= right )) && (( ${data[$left]} <= pivot )); do
left=$(( left + 1 ))
steps=$(( steps + 1 ))
done
while (( right >= left )) && (( ${data[$right]} >= pivot )); do
right=$(( right - 1 ))
steps=$(( steps + 1 ))
done
(( left > right )) && break
temp=${data[$left]}
data[$left]=${data[$right]}
data[$right]=$temp
done
: '$1='"$1" right="$right" 'data[$1]='"${data[$1]}" 'data[$right]='"${data[$right]}"
temp=${data[$1]}
data[$1]=${data[$right]}
data[$right]=$temp
printf -v "$dest" %s "$right"
}
quickSort() {
local partitionPoint
if (( $1 < $2 )); then
partition "$1" "$2" partitionPoint
quickSort "$1" "$(( partitionPoint - 1 ))"
quickSort "$((partitionPoint + 1))" "$2"
fi
}
# involve the algorithm
quickSort 0 "$(( size - 1 ))"
echo "Steps: $steps"
printf '%s\n' "${data[#]}"

Cannot debug simple ksh programme

I wrote this sample KornShell (ksh) code but it is getting bad substitution error during the if clause.
while ((i < $halflen))
do
if [[${strtochk:i:i}==${strtochk:j:j}]];then
i++
j--
else
ispalindrome = false
fi
done
Please help.
NB: I am using ksh88, not ksh93.
shell syntax is very whitespace sensitive:
[[ is acually the name of a command, it's not just syntax, so there must be a space following it.
The last argument of [[ must be ]], so it needs to be preceded by a space.
[[ works differently depending on the number of arguments it receives, so you want to have spaces around ==
In a variable assignment, you must not have spaces around =.
Tips:
once you figure out it's not a palindrome, break out of the while loop
you are probably checking character by character, so you want ${strtochk:i:1}
i++ and j-- are arithmetic expressions, not commands, so you need the double parentheses.
are you starting with i=0 and j=$((${#strtochk} - 1))?
while ((i < halflen))
do
if [[ ${strtochk:i:1} == ${strtochk:j:1} ]];then
((i++))
((j--))
else
ispalindrome=false
break
fi
done
Check if your system has rev, then you can simply do:
if [[ $strtochk == $( rev <<< "$strtochk" ) ]]; then
echo "'$strtochk' is a palindrome"
fi
function is_palindrome {
typeset strtochk=$1
typeset -i i=1 j=${#strtochk}
typeset -i half=$(( j%2 == 1 ? j/2+1 : j/2 ))
typeset left right
for (( ; i <= half; i++, j-- )); do
left=$( expr substr "$strtochk" $i 1 )
right=$( expr substr "$strtochk" $j 1 )
[[ $left == $right ]] || return 1
done
return 0
}
if is_palindrome "abc d cba"; then
echo is a palindrome
fi
You are using ksh88 but the code you tried is using ksh93 feature missing for the 88 version.
You need to replace
if [[${strtochk:i:i}==${strtochk:j:j}]];then
with these portable lines:
if [ "$(printf "%s" "$strtochk" | cut -c $i)" =
"$(printf "%s" "$strtochk" | cut -c $j)" ]; then
and the incorrect:
i++
j--
with:
i=$((i+1))
j=$((j-1))

Bash compare strings not working

I am writing a bash script for a homework and it is required to build a mini library with some functions. I got almost all functions working except the search function. The if statement in line 11 is always true. I don't know how to compare that.
$lib="library"
function search_book {
echo Enter Book Title
read title
exists=`grep "$title" $library | wc -l`
if (( $exists == 0 ))
then
echo "No Such Book in Library"
else
act_owner=`awk -F, '/'$title'/ {print $3}' $library`
echo $act_owner
if (( $act_owner == $lib ))
then
echo Book Kept In $act_owner
else
echo Book Checked Out by $act_owner
fi
fi
stop=0
while (( $stop == 0 ))
do
echo
echo "=========================="
echo "(t) Try again"
echo "(b) Back to main menu"
echo -n 'Choose Option to Continue'
read reply
case $reply in
"t") stop=1; search_book;;
"b") stop=1; main_menu;;
*) echo illegal choice, enter again:
esac
done
}
This if condition seems to be problem:
if (( $act_owner == $lib ))
Since (( and )) are used for arithmetic operations only and it is always evaluating to true because both operators get converted to numbers and if condition becomes (( 0 == 0 )) and that always evaluates to true.
Even this if condition:
(( 'abc' == 0 )) && date
will evaluate to true and print the date for the same reason.
FIX: Change that condition to:
if [[ $act_owner == $lib ]]

Resources