Palindrome using shell scripting - bash

I have tried to write a code for checking a string if it is a palidrome but dont know where am I going wrong
read a
len=`echo $a|wc -m`
len=`expr $len - 1`
# echo $len
flag=1
for((i=0;i<len/2;i++))
do
k=`expr $len - $i - 1`
# echo "${a:$i:1} ${a:$k:1}"
if((${a:$i:1} != ${a:$k:1}))
then
flag=0
break
fi
done
if(($flag == 1))
then
echo Palindrome
else
echo Not Palindrome
fi

Your main problem is that you have used ((..)) instead of [[ .. ]] for the string comparison (spacing matters in the latter).
You can get length directly with ${#a}.
Your algorithm starts from both ends and then increments/decrements towards the middle. So you can get flag implicitly - if you pass the midpoint, the string is a palindrome.
You can replace all use of expr with (( .. )).
for ((..)) allows initialising/updating multiple variables.
read a
for (( i=0, k=${#a}-1; i<=k; i++, k-- ))
do
[[ ${a:$i:1} != ${a:$k:1} ]] && break
done
if (( i>k ))
then
echo Palindrome
else
echo Not Palindrome
fi

Related

Access variables using an external string.[SHELL script]

I currently have code as
if [[ "$FIRSTFLAG" == 1 ]] ; then
all_comp+=("FIRST")
fi
if [[ "$SECONDFLAG" == 1 ]] ; then
all_comp+=("SECOND")
fi
if [[ "$THIRDFLAG" == 1 ]] ; then
all_comp+=("THIRD")
fi
all_comp is just an array
So, im working on a solution to reduce the repetitive code
I know that we can use case here.
I wonder if there is a solution that can be done using array and for loop \
For example(I know its syntactically wrong)
names=("FIRST" "SECOND" "THIRD")
for i in $names[#]; do
if [[ ${i}FLAG == 1 ]]; then <- This line is the issue
all_comp+=("$i")
fi
done
So please tell me if there is a solution for such code example
You need to use indirect expansion by saving the constructed variable name, e.g. iflag=${i}FLAG, then you can use access the indirect expansion with ${!iflag}, e.g.
FIRSTFLAG=1
SECONDFLAG=0
THIRDFLAG=1
all_comp=()
names=("FIRST" "SECOND" "THIRD")
for i in ${names[#]}; do
iflag=${i}FLAG
if [[ ${!iflag} == 1 ]]; then
all_comp+=("$i")
fi
done
echo ${all_comp[#]} # Outputs: FIRST THIRD
Oh another answer, you can make use of the arithmetic expansion operator (( )) i.e.
FIRSTFLAG=1
SECONDFLAG=0
THIRDFLAG=1
all_comp=()
names=("FIRST" "SECOND" "THIRD")
for i in ${names[#]}; do
if (( ${i}FLAG == 1 )); then
all_comp+=("$i")
(( ${i}FLAG = 99 ))
fi
done
echo ${all_comp[#]} # FIRST THIRD
echo $FIRSTFLAG # 99
echo $SECONDFLAG # 0
echo $THIRDFLAG # 99
Reference:
https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion

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?

Determine if Array Has Elements in IF/Then Statement In Bash

I'm trying to determine if an array has elements in an if/then statement like this:
if [[ -n "$aws_user_group" && -n "${#aws_user_roles[#]}" ]]; then
echo "$aws_user_name,$aws_user_group,"${aws_user_roles[*]}",$aws_key,$aws_account_number" >> "$ofile"
elif [[ -n "$aws_user_group" ]]; then
echo "$aws_user_name,$aws_user_group,,$aws_key,$aws_account_number" >> "$ofile"
fi
Problem is, if the array is empty it's represented by '0' as in this debug output:
[[ -n 0 ]]
And the wrong line prints out. It prints the line that includes the 'role' output but leaves it blank.
How can I check a string variable and a mathematical variable in the same if/then statement?
Zero is the number of elements. Use a mathematical evaluation.
if (( ${#aws_user_roles[#]} )) # HAS ELEMENTS
then echo has elements
else echo is empty
fi
example
$: declare -i foo=()
$: if (( ${#foo[#]})); then echo elements; else echo empty; fi
empty
$: foo=( 1 2 3 )
$: if (( ${#foo[#]})); then echo elements; else echo empty; fi
elements
as an aside...
$: foo=( 1 2 3 )
$: if (( ${#foo})); then echo elements; else echo empty; fi
elements
also works, but ${#foo} is really only looking at the string length of first element, and could theoretically give you a false response...though '' returns 1. Hmm...
You can also chain tests for other things -
if (( ${#var} )) && [[ -e "$var" ]] # both must succeed
This is the same as one-liner tests
ls a && ls b || echo one of these doesn't exist...

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))

Comparing the filenames with an integer and reading the filename that is less than the integer

I'm writing a script to read through a folder and compare the file names with a variable.
Both the file name and variable have time as strings as their value.
Ed: filename - 131222 variable = 133000
My folder contains a list of files with similar naming convention, in increasing time order. My variable would fall between any of the two file names. I need to identify the file which is most likely closer(lesser) to the variable.
I'm using bash shell scripting.
How can i do this comparison? I'm using a for loop to iteratively read the filenames in a folder. But i'm clueless on how to do the comparison.
Try using expr ...
filename="123"
ToCompare=100
cmp=`expr $filename - $ToCompare`
if [ $cmp -lt 0 ] ; then
#file has lower value
else
#file has higher value
fi
If I understand...
Use two for cycles, in the first determine the closer file. In the second for choose the file whose "closeness" equals the closer file determined in the first for.
min=1000000000
var=131119
for file in $(ls -1)
do
if [ $var -le $file ]
then
diff=$(($file - $var))
if [ $diff -lt $min ]; then min=$diff; fi
echo "$file - $var = $(($file - $var))"
fi
done
echo $min
for file in $(ls -1)
do
if [ $var -le $file ]
then
diff=$(($file - $var))
if [ $min -eq $diff ]
then
echo "This is your file: $file"
fi
fi
done
A lot of code, though.
If you're sure that all of the files in a directory have only numbers in the filenames, then this is going to work:
need=200 # we're looking for a file that is closest to 200
ls -1 | sort -n | awk "{if(\$1 > $need && prev != \"\") {print ($need-prev < \$1-$need) ? prev : \$1; x=1; exit} prev=\$1} END{if (x != 1) print prev}"
But I highly recommend this one(because it has a bonus filename check!):
#!/bin/bash
need=500
shopt -s nullglob # just in case there are no files at all
for curFile in *; do
if ! [[ $curFile =~ ^[0-9]+$ ]]; then # if there are files that have other symbols besides numbers
echo "Wrong filename format: $curFile"
continue
fi
(( curFile <= need )) && ( ((need - curFile < need - smaller )) || [[ -z $smaller ]] ) && smaller=$curFile
(( curFile >= need )) && ( ((curFile - need < higher - need )) || [[ -z $higher ]] ) && higher=$curFile
done
if [[ -n $smaller ]] && (( need - smaller < higher - need )) || [[ -z $higher ]]; then
echo "$smaller"
else
echo "${higher}"
fi
If you have two files with similar distance (for example 10 and 20 and you're searching for 15) it will output higher number.

Resources