I am running AIX 5.3 using the Korn Shell. I am reading file names from a file, as an example:
E0801260
E0824349
E0925345
EMPMSTR
statement "num=$(expr substr "$DDNAME" 4 2)
extracts the numeric values fine. But when I het the last entry, it returns num=MS, but I get an error messages "The specific number is not valid for this command".
My goal is to read thru this file and skip items like the last one that is NOT numeric. Am I going down the wrong path? Is there a better way of handlling this task?
#!/bin/ksh93
while read LINE
do
sub=${LINE:4:2}
case $sub in
( +([0-9]) ) echo "$sub is a number" ;;
*) echo "$sub is not a number" ;;
esac
done < file
gives
12 is a number
43 is a number
53 is a number
ST is not a number
Thanks everyone for the responses. I have tried freelong's solution and that worked for me. I will also try some of the others to see if they will fit into my design.
I am starting to see entries about ksh93(?) and I will see if I have the lastest Korn release.
Again, thanks all !
---------- Post updated at 08:20 AM ---------- Previous update was at 07:56 AM ----------
I am trying now Scrutinizer's solution and getting an error. I am running ksh93.
num=${DDNAME:3:2}
if [[ $num =~ [0-9]+ ]]
then
echo "$num is numeric"
else
echo "$num is NOT numeric"
fi
Error: ./kwb006[29]: 0403-057 Syntax error at line 32 : `=~' is not expected.
Humm, whether your shell scripts are portable or not mostly depends on how you write your shell scripts and not on the particular shell you choose to use.
Every modern shell contains a core set of functionality which is portable across most platforms and architectures together with extended functionality which certain groups of users find useful.
Provided a shell script is written to only use the core functionality, it will generally be portable.
In AIX (including 5.3) /bin/ksh is a ksh88, whereas /bin/ksh93 is a ksh93. The substr()-function in the form "${var:start:length}" is only implemented in ksh93.
cfajohnsons observation is correct in a very general way: using ksh93-functions always runs the risk of getting less portable. On the other hand: using an external tool (expr, sed, awk, whatever) to trim the variables content is expensive in terms of system calls. I would like to offer the following solution to this problem, which which work in every ksh.version:
sub1=${DDNAME%${DDNAME#?????}}
sub=${sub1#???}
The first line extracts the first 5 characters from the string $DDNAME, the second statement extracts the last 2 from these 5 characters, effectively giving the substring starting at pos 4, length 2. Because this is done completely in the shell and without calling an external program it shoulld be by far faster than any solution based on such a program.
cfajohnson already explained how this can be tested for being an integer or not.
The following substr functions will work in any POSIX shell and don't use any external commands.
_substr()
{
_SUBSTR=
## store the parameters
ss_str=$1
ss_first=$2
ss_length=${3:-${#ss_str}}
## return an error if the first character wanted is beyond end of string
if [ $ss_first -gt ${#ss_str} ]
then
return 1
fi
if [ $ss_first -gt 1 ]
then
## build a string of question marks to use as a wildcard pattern
_repeat "?" $(( $ss_first - 1 ))
## remove the beginning of string
ss_str=${ss_str#$_REPEAT}
elif [ ${ss_first} -lt 0 ] ## ${#ss_str} ]
then
## count from end of string
_repeat "?" ${ss_first#-}
## remove the beginning
ss_str=${ss_str#${ss_str%$_REPEAT}}
fi
## ss_str now begins at the point we want to start extracting
## print the desired number of characters
if [ ${#ss_str} -gt $ss_length ]
then
_repeat "${ss_wild:-??}" $ss_length
ss_wild=$_REPEAT
_SUBSTR=${ss_str%${ss_str#$ss_wild}}
else
_SUBSTR=${ss_str}
fi
}
substr()
{
_substr "$@" && printf "%s\n" "$_SUBSTR"
}
_repeat()
{
## If the first argument is -n, repeat the string N times
## otherwise repeat it to a length of N characters
case $1 in
-n) shift
r_num=$(( ${#1} * $2 ))
;;
*) r_num=$2
;;
esac
r_str=$1
_REPEAT=$1
while [ ${#_REPEAT} -lt ${r_num} ]
do
if [ $(( ${#_REPEAT} * 2 )) -gt $r_num ]
then
while [ ${#_REPEAT} -lt $r_num ]
do
_REPEAT=$_REPEAT$r_str
done
elif [ $(( ${#_REPEAT} * 2 )) -eq $r_num ]
then
_REPEAT=$_REPEAT$_REPEAT
else
## The length builds rapidly by concatenating 3 copies in each loop
## Your results may be different, but I have found that three is
## the optimum number; try it with more, if you like
_REPEAT=$_REPEAT$_REPEAT$_REPEAT
fi
done
while [ ${#_REPEAT} -gt $r_num ]
do
_REPEAT=${_REPEAT%?}
done
}
I have not tested this, but if your input data only contains letters and numbers then maybe something like this would work.
extractNumbers()
{
oldIFS="$IFS"
IFS="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
set -- $1
IFS=${oldIFS}
unset oldIFS # Old Bourne Shells do not support typeset on variable definiions
# remove empty arguments
for a in $*
do
# or add logic to use arithmetic operators to extract your number portion or whatever else you want before
# echoing the result back
echo $1
break
done
}