A simple way to detect an odd or even number in bash

For discussions about programming, programming questions/advice, and projects that don't really have anything to do with Puppy.
Message
Author
User avatar
technosaurus
Posts: 4853
Joined: Mon 19 May 2008, 01:24
Location: Blue Springs, MO
Contact:

#16 Post by technosaurus »

MochiMoppel wrote:For a bullet proof check this should do:

Code: Select all

[[ $N =~ ^-?[0-9]+$ ]] || exec echo $N is not an integer
for a non bashism version (as long as parameters don't contain spaces)

Code: Select all

isnumber() [[ $(($1+0)) = ${1} ]]
Check out my [url=https://github.com/technosaurus]github repositories[/url]. I may eventually get around to updating my [url=http://bashismal.blogspot.com]blogspot[/url].

musher0
Posts: 14629
Joined: Mon 05 Jan 2009, 00:54
Location: Gatineau (Qc), Canada

#17 Post by musher0 »

MochiMoppel wrote:@musher0: Your original idea to use a case statement wasn't bad. While checking
only the last digit doesn't tell you if you are dealing with an integer, you could easily
adapt your case conditions to allow only integers and issue a warning if none of the
conditions match. In the end such construct is not more complex than using bash
arithmetic operators and doesn't require knowledge on how modulo or bash division
work.

Using modulo and bash:

Code: Select all

[[ $N = *[^0-9-]* ]] && echo $N is not an integer && exit
(($N%2)) && echo $N is odd ||  echo $N is even
Note: The integer check avoids regex, but it doesn't catch oddly placed minus signs.

For a bullet proof check this should do:

Code: Select all

[[ $N =~ ^-?[0-9]+$ ]] || exec echo $N is not an integer
.
Hello MochiMopei.

As I said above, I evolved this idea in the context of a list of files that I needed to
split in two "halves" (+/- one line) with head and tail. Now wc -l does not care if the
last line is only half full or a quarter full!! It was not even a text, just a list of files.
I did not need to check for decimals except if the total of lines is odd.

I am a man of the field, not a theorist. I respond to practical situations I encounter.
I was not expecting to have so many replies! But you guys with more knowledge
than me, please continue; this discussion has become most interesting.

Bye for now.

~~~~~~~~~~~~~~
Sorry to disappoint everyone (!), but this is the script where I originally encountered
this odd/even problem:

Code: Select all

#!/bin/sh
# ~/.pekwm/defaults-pekwm.sh
#
# Goal: create a pekwm sub-menu in two +/- equal parts for Puppy's' /usr/local/bin/default* files.
#
# © Christian L'Écuyer, Gatineau (Qc), Canada, 2018/02/18. GPL3
# (Alias musher0 [forum Puppy].) # Rév. : 2018/02/20
#################   # https://opensource.org/licenses/GPL-3.0
#    This program is free software: you can redistribute it and/or modify it under the
#    terms of the GNU General Public License as published by the Free Software
#    Foundation, either version 3 of the License, or  (at your option) any later version.
#         This program is distributed in the hope that it will be useful, but WITHOUT ANY
#    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
#    A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#         You should have received a copy of the GNU General Public License along with
#   this program. If not, see <http://www.gnu.org/licenses/>.
##########
#   Ce programme est libre : vous pouvez le redistribuer ou modifier selon les termes
#   de la Licence Publique Générale GNU publiée par la Free Software Foundation (v. 3
#   ou toute version ultérieure choisie par vous).
#       Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE
#   GARANTIE, ni explicite ni implicite, y compris des garanties de commercialisation
#   ou d'adaptation à un but spécifique. Pour plus de détails, veuillez vous reporter
#   au texte officiel de cette licence à https://opensource.org/licenses/GPL-3.0, à
#   http://www.linux-france.org/article/these/gpl.html pour une traduction et, pour une
#   explication en français, à https://fr.wikipedia.org/wiki/Licence_publique_générale_GNU.
################
#### set -xe
cd ~/.pekwm
LC_ALL=C
ProgLine () { echo -e "\t\tEntry = "${line#*t}" { Actions = "Exec $line &" }" >> ~/.pekwm/menu.Defaults.tmp2; }

AppS=/usr/local/bin
cd $AppS
ls -Algo default* | awk '$1 !~ /lrw/ && $NF !~ /lts-|run|cdr|handler/ { print $NF }' > ~/.pekwm/default-progs

echo "}
# Ferme le sous-menu précédent." > ~/.pekwm/menu.Defaults

case "${LANG:0:2}" in
     fr)echo "Submenu = "Applis par défaut" {" >> ~/.pekwm/menu.Defaults ;;
     en|*)echo "Submenu = "Default Apps" {" >> ~/.pekwm/menu.Defaults ;;
esac

> ~/.pekwm/menu.Defaults.tmp2
while read line;do ProgLine;done < ~/.pekwm/default-progs
case "${LANG:0:2}" in
     fr) # Il est plus pratique de traduire avant de faire la division.
          RPLCT="replaceit --input=/root/.pekwm/menu.Defaults.tmp2"
$RPLCT archiver archiveur "}default" "{Entry";$RPLCT audiomixer mixeur "}default" "{Entry"
$RPLCT browser navigateur "}default" "{Entry";$RPLCT calendar calendrier "}default" "{Entry"
$RPLCT chat clavardage "}default" "{Entry";$RPLCT connect connexion "}default" "{Entry"
$RPLCT draw "ed. de dessin" "}default" "{Entry";$RPLCT email courriel "}default" "{Entry"
$RPLCT paint "ed. d'image" "}default" "{Entry";$RPLCT screenshot captures "}default" "{Entry"
$RPLCT spreadsheet chiffrier "}default" "{Entry";$RPLCT audioeditor "ed. audio" "}default" "{Entry"
$RPLCT audioplayer "lect. audio" "}default" "{Entry";$RPLCT cdplayer "lect. CD" "}default" "{Entry"
$RPLCT cdrecorder enregistreur "}default" "{Entry";$RPLCT chmviewer "vis. de chm" "}default" "{Entry"
$RPLCT filemanager "gest. de fich." "}default" "{Entry";$RPLCT htmleditor "ed. de html" "}default" "{Entry"
$RPLCT pdfviewer "vis. de pdf" "}default"" "{Entry";$RPLCT htmlviewer "vis. de html" "}default" "{Entry"
$RPLCT imageviewer "vis. d'image" "}default" "{Entry";$RPLCT mediaplayer "lect. media" "}default" "{Entry"
$RPLCT processmanager "gest. de proc." "}default" "{Entry"
$RPLCT texteditor "ed. de texte" "}default" "{Entry";$RPLCT textviewer "vis. de texte" "}default" "{Entry"
$RPLCT wordprocessor "tr. de texte" "}default" "{Entry"
          sort ~/.pekwm/menu.Defaults.tmp2 > ~/.pekwm/menu.Defaults.tmp ;; # Tri nécessaire
     en|*)
          mv ~/.pekwm/menu.Defaults.tmp2 ~/.pekwm/menu.Defaults.tmp ;;
esac
echo "Submenu = "1..." {" >> ~/.pekwm/menu.Defaults

# Vérifier si nlign est impair.
nlign="`cat ~/.pekwm/menu.Defaults.tmp | wc -l`"
case "${nlign: -1}" in # Si nombre impair, on aura un tuilage dans les entrées, # ;-) to MochiMoppei!
     1|3|5|7|9)nlign="`expr $nlign + 1`" ;;
esac
nlign="`expr $nlign \/ 2`" # mais on peut alors diviser sans reste.
#
head -n "$nlign"  /root/.pekwm/menu.Defaults.tmp >> ~/.pekwm/menu.Defaults
echo -e "\t}" >> ~/.pekwm/menu.Defaults
echo "Submenu = "2..." {" >> ~/.pekwm/menu.Defaults
tail -n "$nlign"  /root/.pekwm/menu.Defaults.tmp >> ~/.pekwm/menu.Defaults
echo -e "\t}\n}" >> ~/.pekwm/menu.Defaults

rm -f ~/.pekwm/menu.Defaults.tmp*
rm -f ~/.pekwm/default-progs

LC_ALL="" # set +xe
Scrots of the results are attached.

Also, these are the submenus (English version) that the above script creates,
which are read by the pekwm menu routine:

Code: Select all

}
# Ferme le sous-menu précédent.
Submenu = "Default Apps" {
Submenu = "1..." {
		Entry = "archiver" { Actions = "Exec defaultarchiver &" }
		Entry = "audioeditor" { Actions = "Exec defaultaudioeditor &" }
		Entry = "audiomixer" { Actions = "Exec defaultaudiomixer &" }
		Entry = "audioplayer" { Actions = "Exec defaultaudioplayer &" }
		Entry = "browser" { Actions = "Exec defaultbrowser &" }
		Entry = "calendar" { Actions = "Exec defaultcalendar &" }
		Entry = "cdplayer" { Actions = "Exec defaultcdplayer &" }
		Entry = "chat" { Actions = "Exec defaultchat &" }
		Entry = "chmviewer" { Actions = "Exec defaultchmviewer &" }
		Entry = "connect" { Actions = "Exec defaultconnect &" }
		Entry = "contact" { Actions = "Exec defaultcontact &" }
		Entry = "draw" { Actions = "Exec defaultdraw &" }
		Entry = "email" { Actions = "Exec defaultemail &" }
		Entry = "filemanager" { Actions = "Exec defaultfilemanager &" }
	}
Submenu = "2..." {
		Entry = "filemanager" { Actions = "Exec defaultfilemanager &" }
		Entry = "htmleditor" { Actions = "Exec defaulthtmleditor &" }
		Entry = "htmlviewer" { Actions = "Exec defaulthtmlviewer &" }
		Entry = "imageviewer" { Actions = "Exec defaultimageviewer &" }
		Entry = "mediaplayer" { Actions = "Exec defaultmediaplayer &" }
		Entry = "paint" { Actions = "Exec defaultpaint &" }
		Entry = "pdfviewer" { Actions = "Exec defaultpdfviewer &" }
		Entry = "processmanager" { Actions = "Exec defaultprocessmanager &" }
		Entry = "screenshot" { Actions = "Exec defaultscreenshot &" }
		Entry = "spreadsheet" { Actions = "Exec defaultspreadsheet &" }
		Entry = "terminal" { Actions = "Exec defaultterminal &" }
		Entry = "texteditor" { Actions = "Exec defaulttexteditor &" }
		Entry = "textviewer" { Actions = "Exec defaulttextviewer &" }
		Entry = "wordprocessor" { Actions = "Exec defaultwordprocessor &" }
	}
}
Attachments
pekwm-submenu-1-for-defaults.jpg
(94.67 KiB) Downloaded 289 times
pekwm-submenu-2-for-defaults.png
The overlap is intentional, between the first entry of this one and the last entry of the first one. (&quot;Gest. de fich.&quot; is abbreviated French for &quot;filemanager&quot;.)
(55.32 KiB) Downloaded 280 times
Last edited by musher0 on Fri 23 Feb 2018, 04:57, edited 3 times in total.
musher0
~~~~~~~~~~
"You want it darker? We kill the flame." (L. Cohen)

User avatar
MochiMoppel
Posts: 2084
Joined: Wed 26 Jan 2011, 09:06
Location: Japan

#18 Post by MochiMoppel »

musher0 wrote:I am a man of the field, not a theorist. I respond to practical situations I encounter.
I was trying to defend your solution, not dismiss it.

musher0
Posts: 14629
Joined: Mon 05 Jan 2009, 00:54
Location: Gatineau (Qc), Canada

#19 Post by musher0 »

MochiMoppel wrote:
musher0 wrote:I am a man of the field, not a theorist. I respond to practical situations I encounter.
I was trying to defend your solution, not dismiss it.
I know! And I thank you for it.
musher0
~~~~~~~~~~
"You want it darker? We kill the flame." (L. Cohen)

User avatar
MochiMoppel
Posts: 2084
Joined: Wed 26 Jan 2011, 09:06
Location: Japan

#20 Post by MochiMoppel »

technosaurus wrote:for a non bashism version (as long as parameters don't contain spaces)

Code: Select all

isnumber() [[ $(($1+0)) = ${1} ]]
AFAIK double brackets are not POSIX compliant though bash supports them also in its "more closely to the POSIX standard" mode.

User avatar
technosaurus
Posts: 4853
Joined: Mon 19 May 2008, 01:24
Location: Blue Springs, MO
Contact:

#21 Post by technosaurus »

MochiMoppel wrote:
technosaurus wrote:for a non bashism version (as long as parameters don't contain spaces)

Code: Select all

isnumber() [[ $(($1+0)) = ${1} ]]
AFAIK double brackets are not POSIX compliant though bash supports them also in its "more closely to the POSIX standard" mode.
crap, you are right, always get that backwards - it should still work if you change them to single brackets as long as the parameters are quoted or don't contain spaces, newlines or tabs. FWIW both busybox shells (ash and hush) support both forms.
Check out my [url=https://github.com/technosaurus]github repositories[/url]. I may eventually get around to updating my [url=http://bashismal.blogspot.com]blogspot[/url].

User avatar
MochiMoppel
Posts: 2084
Joined: Wed 26 Jan 2011, 09:06
Location: Japan

#22 Post by MochiMoppel »

technosaurus wrote:it should still work if you change them to single brackets as long as the parameters are quoted or don't contain spaces, newlines or tabs
No, it does not work
- A single bracket is an ordinary command. The function requires curly braces.
- $(($1+0)) will generate an irrecoverable syntax error if the parameter contains a decimal separator, which makes this test unsuitable as an integer check. Same happens with double brackets.

The following code should work better. Spaces etc. in parameters will lead to a correct "not an integer" result
Decimal separators will not generate a fatal syntax error.

Code: Select all

isnumber() { test "$@" -eq "$@"  2>/dev/null ;}

User avatar
technosaurus
Posts: 4853
Joined: Mon 19 May 2008, 01:24
Location: Blue Springs, MO
Contact:

#23 Post by technosaurus »

MochiMoppel wrote:The function requires curly braces.
It can be any compound statement which also includes a subshell "( ... )" in Posix and "[[ ... ]]" in bash, ksh (since 1988) and any modern shell including busybox and just about everything but dash. I didn't realize that even with "{ ... }", the compound statement can also be followed by redirection of stdin, stdout, stderr (useful for logging). The double bracket version does have some subtle differences though. I seem to have gotten a bit rusty on my shell programming. Likely because I have been using C primarily for the last few years and mostly awk right before that. Perhaps I should write myself a manual as I relearn it for the next time I forget. Tentatively titled "The Portable Shell Scripting Guide" as an answer to "The Advanced Bash Scripting Guide" ... DashAsBinSh mainly describes pitfalls without offering solutions
Check out my [url=https://github.com/technosaurus]github repositories[/url]. I may eventually get around to updating my [url=http://bashismal.blogspot.com]blogspot[/url].

User avatar
NeroVance
Posts: 201
Joined: Wed 10 Oct 2012, 23:00
Location: Halifax, Canada

#24 Post by NeroVance »

musher0 wrote:
puppy_apprentice wrote:When i was learning Pascal in high school i was using this formula before i noticed that Pascal has odd function built-in.

Code: Select all

#!/bin/sh
check=`expr $1 / 2 \* 2`
if [ $check != $1 ]
then
  echo "$1 is odd"
else
  echo "$1 is even"
fi
You learned Pascal in high school? You must be very old?!?! :lol:
Oddly enough I'm only 24, but my older sister learned Pascal in High School from what I recall.

musher0
Posts: 14629
Joined: Mon 05 Jan 2009, 00:54
Location: Gatineau (Qc), Canada

#25 Post by musher0 »

That's funny. Being 24 is not odd! :D Being 24 is being young!

I was saying that because Pascal is an old computer language,
as computer languages go.
musher0
~~~~~~~~~~
"You want it darker? We kill the flame." (L. Cohen)

jafadmin
Posts: 1249
Joined: Thu 19 Mar 2009, 15:10

#26 Post by jafadmin »

Check the least significant bit: ((($MyVal&1)==0))

Code: Select all

  MyVal=221;((($MyVal&1)==0)) && echo "$MyVal is even" || echo "$MyVal is odd"
Or ...

Code: Select all

if (($MyVal&1))
then 	echo "$MyVal is odd"
else 	echo "$MyVal is even"
fi
In C:

Code: Select all

int MyVal = 332;
printf( "Value of MyVal is %s\n", (MyVal&1) ? "odd": "even" );

Post Reply