checking if a string is a palindrome - linux

I am trying to check if a string is a palindrome in bash. Here is what I came up with:
#!/bin/bash
read -p "Enter a string: " string
if [[ $string|rev == $string ]]; then
echo "Palindrome"
fi
Now, echo $string|rev gives reversed string. My logic was to use it in the condition for if. That did not work out so well.
So, how can I store the "returned value" from rev into a variable? or use it directly in a condition?

Another variation without echo and unnecessary quoting within [[ ... ]]:
#!/bin/bash
read -p "Enter a string: " string
if [[ $(rev <<< "$string") == "$string" ]]; then
echo Palindrome
fi

A bash-only implementation:
is_palindrome () {
local word=$1
local len=$((${#word} - 1))
local i
for ((i=0; i <= (len/2); i++)); do
[[ ${word:i:1} == ${word:len-i:1} ]] || return 1
done
return 0
}
for word in hello kayak; do
if is_palindrome $word; then
echo $word is a palindrome
else
echo $word is NOT a palindrome
fi
done
Inspired by gniourf_gniourf:
is_palindrome() {
(( ${#1} <= 1 )) && return 0
[[ ${1:0:1} != ${1: -1} ]] && return 1
is_palindrome ${1:1: 1}
}
I bet the performance of this truly recursive call really sucks.

Use $(command substitution):
#!/bin/bash
read -p "Enter a string: " string
if [[ "$(echo "$string" | rev)" == "$string" ]]; then
echo "Palindrome"
fi

Maybe it is not the best implementation, but if you need something with pure sh
#!/bin/sh
#get character <str> <num_of_char>. Please, remember that indexing is from 1
get_character() {
echo "$1" | cut -c "$2"
}
for i in $(seq $((${#1} / 2))); do
if [ "$(get_character "$1" "$i")" != "$(get_character "$1" $((${#1} - i + 1)))" ]; then
echo "NO"
exit 0
fi
done
echo "YES"
and canonical way with bash as well
for i in $(seq 0 $((${#1} / 2 - 1))); do
if [ "${1:$i:1}" != "${1:$((${#1} - i - 1)):1}" ]; then
echo "NO"
exit 0
fi
done
echo "YES"

Skipping all punctuation marks and letter case.
input:He lived as a devil, eh?
output:Palindrome
input:Madam, I am Adam.
output:Not Palindrome
#!/bin/bash
#set -x
read -p "Enter a sentence" message
message=$(echo "$message" | \
sed -e '
s/[[:space:]]//g
s/[[:punct:]]//g
s/\!//g
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
' )
i=0
while read -n 1 letter
do
tempArray[i]="$letter"
((i++))
done < <(echo "$message")
i=0
counter=$((${#message}-1))
while [ "$i" -ne $((${#message}/2)) ]
do
if [ "${tempArray[$i]}" = "${tempArray[$counter]}" ]
then
((i++))
((counter--))
else echo -n "Not ";break
fi
done
echo "Palindrome"
exit

Related

Shell script if condition not evaluated for a help info display for the user

I had written a help menu for reference about the usage of a shell script my_script.sh
echo $'\n\n'
echo $(printf '=%.0s' {1..100})
printf ' %.0s' {1..40}
echo "Welcome"
echo $(printf '=%.0s' {1..100})
echo $'\n'
arg=$1
echo "Input : $arg"
echo
if [[ arg -eq "-h" ]] || [[ arg -eq "-H" ]] || [[ arg -eq "-help" ]] || [[ arg -eq "-Help" ]] || [[ arg -eq "--h" ]] || [[ arg -eq "--H" ]] || [[ arg -eq "--help" ]] || [[ arg -eq "--Help" ]]; then
echo "Help menu requested...."
echo $'\n\n'
echo $(printf '~%.0s' {1..100})
printf ' %.0s' {1..43}
echo "Help Menu"
echo $(printf '~%.0s' {1..100})
echo $'\n'
exit 0
else
echo "Executing a program...."
./another_script.sh
fi
When I execute `myscript.sh -h' (or any of the '-' prefixed option), it goes to the if condition, but any other argument doesn't. What am I doing wrong here? I'm new to bash scripts.
Two simple problems with your if:
-eq is for integer comparison, = or == for strings
Use $arg in your if (instead of arg).
But: I would recommend using getopts instead of string comparison. This would make the part more robust, taking care of different ordering of parameters, or when one letter parameters are combined into a single argument.
Unfortunately I do not know the exact reason why your code does not work, but I can offer you a quick fix: You can write "==" instead of "-eq" and prefix your variable "arg" with a dollar sign. Then your script should work fine.
Working example (GNU bash 4.4.19):
arg=$1
if [[ $arg == "-h" ]] || [[ $arg == "-H" ]]; then
echo "Help!"
else
echo "Stop!"
fi
the -eq operation is only used for comparing numbers.
To compare strings uses the operation =
You forgot $ sign for variables arg in Bash, should be $arg
When we use variables in Bash, we should better use double quote.
use #() for multiple strings comparison.
so the if conditions [[ arg -eq "-h" ]] should be [[ "$arg" = "-h" ]]
When comparing a variable with multiple strings, we can use [[ "$arg" = #(-h|-H|--help|--HELP|--h|--H|-help|--HELP) ]].
if [[ "$arg" = #(-h|-H|--help|--HELP|--h|--H|-help|--HELP) ]]; then
echo "Help menu requested...."
echo $'\n\n'
echo $(printf '~%.0s' {1..100})
printf ' %.0s' {1..43}
echo "Help Menu"
echo $(printf '~%.0s' {1..100})
echo $'\n'
exit 0
else
echo "Executing a program...."
./another_script.sh
fi
In addition, we can use boxes(boxes - Command line ASCII boxes unlimited!
) to generate a comment box
cat <<EOF | boxes -a c -d shell -p a5 -s 30x9
HELP MENU
bla bla
EOF
output:
########################################
# #
# #
# HELP MENU #
# #
# bla bla #
# #
# #
########################################

If statement 3 conditions Bash

I want to do an if statement with three conditions that have to be satisfied at the same time. I am using Ubuntu Bash for Windows and the values $c1, $c2 and $c3 are non-integer (decimal negative numbers).
if [ (( $(echo "$c1 < 0" | bc -l) )) ] && [ (( $(echo "$c2 < 0" | bc -l) )) ] && [ (( $(echo "$c3 < 0" | bc -l) )) ];
then
>&2 echo -e " ++ Constraints OK"
else
>&2 echo -e " ++ Constraints WRONG"
fi
However, I get the following syntax error in the if line: syntax error near unexpected token `('
If I just put one condition:
if (( $(echo "$c1 < 0" | bc -l) ));
it works, but when I add the three of them as AND (&&), I get the error. Can anyone help me?
Considerably more efficient (assuming you know your values are numbers, and only need to check whether they're all negative) would be:
if [[ $c1 = -* ]] && [[ $c2 = -* ]] && [[ $c3 = -* ]]; then
>&2 echo " ++ Constraints OK"
else
>&2 echo " ++ Constraints WRONG"
fi
If you want to be more specific about the permissible formats (f/e, allowing leading spaces), a regex is another option, which similarly can be implemented more efficiently than spawning a series of subshells invoking bc:
nnum_re='^[[:space:]]*-([[:digit:]]*[.][[:digit:]]+|[[:digit:]]+)$'
if [[ $c1 =~ $nnum_re ]] && [[ $c2 =~ $nnum_re ]] && [[ $c3 =~ $nnum_re ]]; then
>&2 echo " ++ Constraints OK"
else
>&2 echo " ++ Constraints WRONG"
fi
First, pass the relational AND operators into bc to get rid of some punctuation (also only invokes bc once):
if (( $(echo "$c1 < 0 && $c2 < 0 && $c3 < 0" | bc -l) == 1 ))
then
>&2 echo -e " ++ Constraints OK"
else
>&2 echo -e " ++ Constraints WRONG"
fi
Although if it were me, I would create a shell function returning a "true" exit status if bc evaluates the result of an expression to non-zero. Then you can hide most of the ugly punctuation in one place separated from your main logic:
function bc_true() {
(( $(echo "$#" | bc -l) != 0 ))
}
And write a (IMO) cleaner shell expression:
if bc_true "$c1 < 0 && $c2 < 0 && $c3 < 0"
then
...

Adding exceptions during cut returns unwanted results

I have a file that is being generated (sort of an audit file) with who have accessed said file. Looks as follows:
I need to write an alarming system that enters said report and checks for all users. however bash for some reason interprets the "------" as an input.
for i in $(cut -c 8-13 report_file.csv)
do
if [[ $i -eq 'suser' ]] || [[ $i -eq '--------' ]] || [[ $i -eq 'login' ]] || $i -eq 'root']]
then
break
else
echo "email text"+ $i | mailx -s "email subject" $EMAILS_LIST
done
the output for this is:
./script_name.sh: line 26: [[: --------: syntax error: operand
expected (error token is "-")
So as I understand it takes the exception "------" and still sees it as sort of input.
So, what am I missing?
-eq in test (same in extended test [[...]]) is an operator for integers. '---------' is not an integer. Use = to compare strings.
... [[ "$i" = 'suser' ]] || [[ "$i" = '--------' ]] || [[ "$i" = 'login' ]] || [[ "$i" = 'root' ]]
or simpler
... [[ "$i" = 'suser' || "$i" = '--------' || "$i" = 'login' || "$i" = 'root' ]]
or simpler:
case "$i" in
suser|--------|login|root) ;;
*) echo "email text"+ $i | mailx -s "email subject" $EMAILS_LIST; ;;
esac
Side note:
Reading lines from file using for i in $(...) is bad. It's better to use while read -r line; do .... done < <(cut -c 8-13 report_file.csv) or cut -c 8-13 report_file.csv | while read -r line; do ... done see here.

When/how to use "==" or "-eq" operator in test?

In the following code I want to compare the command line arguments with the parameters but I am not sure what is the current syntax to compare the arguments with parameters..i.e "==" or "-eq".
#!/bin/bash
argLength=$#
#echo "arg = $1"
if [ argLength==0 ]; then
#Running for the very first
#Get the connected device ids and save it in an array
N=0
CONNECTED_DEVICES=$(adb devices | grep -o '\b[A-Za-z0-9]\{8,\}\b'|sed -n '2,$p')
NO_OF_DEVICES=$(echo "$CONNECTED_DEVICES" | wc -l)
for CONNECTED_DEVICE in $CONNECTED_DEVICES ; do
DEVICE_IDS[$N]="$CONNECTED_DEVICE"
echo "DEVICE_IDS[$N]= $CONNECTED_DEVICE"
let "N= $N + 1"
done
for SEND_DEVICE_ID in ${DEVICE_IDS[#]} ; do
callCloneBuildInstall $SEND_DEVICE_ID
done
elif [ "$1" -eq -b ]; then
if [ $5 -eq pass ]; then
DEVICE_ID=$3
./MonkeyTests.sh -d $DEVICE_ID
else
sleep 1h
callCloneBuildInstall $SEND_DEVICE_ID
fi
elif [ "$1" -eq -m ]; then
echo "Check for CloneBuildInstall"
if [ "$5" -eq pass ]; then
DEVICE_ID=$3
callCloneBuildInstall $SEND_DEVICE_ID
else
echo "call CloneBuildInstall"
# Zip log file and save it with deviceId
callCloneBuildInstall $SEND_DEVICE_ID
fi
fi
function callCloneBuildInstall {
./CloneBuildInstall.sh -d $SEND_DEVICE_ID
}
From help test:
[...]
STRING1 = STRING2
True if the strings are equal.
[...]
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
But in any case, each part of the condition is a separate argument to [.
if [ "$arg" -eq 0 ]; then
if [ "$arg" = 0 ]; then
Why not use something like
if [ "$#" -ne 0 ]; then # number of args should not be zero
echo "USAGE: "
fi
When/how to use “==” or “-eq” operator in test?
To put it simply use == when doing lexical comparisons a.k.a string comparisons but use -eq when having numerical comparisons.
Other forms of -eq (equal) are -ne (not equal), -gt (greater than), -ge (greater than or equal), -lt (lesser than), and -le (lesser than or equal).
Some may also suggest preferring (( )).
Examples:
[[ $string == "something else" ]]
[[ $string != "something else" ]] # (negated)
[[ $num -eq 1 ]]
[[ $num -ge 2 ]]
(( $num == 1 ))
(( $num >= 1 ))
And always use [[ ]] over [ ] when you're in Bash since the former skips unnecessary expansions not related to conditional expressions like word splitting and pathname expansion.

how to use if to see whether file has suffix in shell bash script [duplicate]

This question already has answers here:
How to check if a string contains a substring in Bash
(29 answers)
Closed 9 years ago.
if[ xxx ]
how to expresss the string or file include '.'
I am new to study shell,thanks for any help
You can use the matching operator:
$ if [[ "abc.def" =~ \. ]]; then echo "yes"; else echo "no"; fi
yes
$ if [[ "abcdef" =~ \. ]]; then echo "yes"; else echo "no"; fi
no
This matches if the dot is the first or last (or only) character in the string. If you expect characters on both sides of the dot, you can do the following:
$ if [[ "ab.cdef" =~ .\.. ]]; then echo "yes"; else echo "no"; fi
yes
$ if [[ ".abcdef" =~ .\.. ]]; then echo "yes"; else echo "no"; fi
no
$ if [[ "abcdef." =~ .\.. ]]; then echo "yes"; else echo "no"; fi
no
You can also use pattern matching:
$ if [[ "ab.cdef" == *?.?* ]]; then echo "yes"; else echo "no"; fi
yes
$ if [[ ".abcdef" == *?.?* ]]; then echo "yes"; else echo "no"; fi
no
$ if [[ "abcdef." == *?.?* ]]; then echo "yes"; else echo "no"; fi
no
A good reference for both patterns and regexes is at Greg's Wiki
bash supports glob-style pattern matching:
if [[ "$file" = *?.?* ]]; then
...
fi
Note that this assumes a prefix as well - this also ensures that it will not match the . and .. directories.
If you want to check for a specific extension:
if [[ "$file" = *?.foo ]]; then
...
fi
echo "xxx.yyy" | grep -q '\.'
if [ $? = 0 ] ; then
# do stuff
fi
Or
echo "xxx.yyy" | grep -q '\.' && <one statement here>
#e.g.
echo "xxx.yyy" | grep -q '\.' && echo "got a dot"

Resources