2

I am receiving numeric variables sometimes with 2, other times with 3 digits, like '321' and '32'. And I want to put a dot among each of the numbers. So if I receive '32', I got to echo '3.2' and if I receive '321' I echo '3.2.1'.

This is what I did:

S='321'
SL="${#S}" #string lentgh

n1=`echo $S | cut -c 1-1`
n2=`echo $S | cut -c 2-2`

if [ "$SL" -eq 2 ]; then
    echo $n1.$n2
elif  [ "$SL" -eq 3 ]; then
    n3=`echo $S | cut -c 3-3`
    echo $n1.$n2.$n3
else
    die 'Works only with 2 or 3 digits'
fi

My question is: is there any shorter way of doing the same thing?


UPDATE: Shorter but still verbose:

SL="${#1}" #string lentgh
S=$1
if [ "$1" -eq 3 ]; then
    $n3=".${S:2:1}"
fi
if  [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
    die 'Works only with 2 or 3 digits'
fi

echo "${S:0:1}.${S:1:1}$n3"

UPDATE 1:

If I include the if block, the sed+regex version will be quite as long as the pure bash version:

SL="${#1}" #string lentgh
S=$1
N=$(echo $S | sed -r "s/([0-9])/\1./g")
echo ${N%%.}
if  [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
    die 'Works only with 2 or 3 digits'
fi

Or, using a one line sed+regex with two expressions:

SL="${#1}" #string lentgh
echo $1 | sed -e 's/\([[:digit:]]\)/.\1/g' -e 's/^\.//'
if  [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
    die 'Works only with 2 or 3 digits'
fi

Thanks.

phoxis
  • 60,131
  • 14
  • 81
  • 117
Roger
  • 8,286
  • 17
  • 59
  • 77

4 Answers4

2

Here is one. This will work for any string length.

#!/bin/bash

#s is the string
#fs is the final string

echo "Enter string"
read s

n="${#s}"
fs=""

i=0
for ((i=0; i<n; i++))
  do
   fs="$fs.${s:i:1}"
done

#find the length of the final string and
#remove the leading '.' 

n="${#fs}"
fs="${fs:1}"

echo "$fs"
phoxis
  • 60,131
  • 14
  • 81
  • 117
  • This is beautiful. What if I knew I always would receive 2 or 3 digits? I guess your code could be shorter, am I right? – Roger Aug 19 '11 at 11:14
  • i have used bash builtins to achieve this. In the case of the input length 2 or 3 you just insert an `if - else` like `n` is not `3` or `2` the process will stop, else it will continue to the loop. And if you simply want to statically code the 2 or 3 length strings, then you can simply remove the loop and manually code for 2 and 3 length codes (as you have done in your code in the question). Or use other solutions by others. – phoxis Aug 19 '11 at 11:17
2

It's not that pretty, but at least it's short:

num=$(echo $S | sed -r "s/([0-9])/\1./g")
echo ${num%%.}
carlpett
  • 12,203
  • 5
  • 48
  • 82
  • Ugly and sharp, indeed. Could you just give any clue of what is really happening in this code? I got an error: "num: command not found" – Roger Aug 19 '11 at 11:15
  • Well, now that I included the pipe that I forgot it might acually work... Sorry about that. The sed command will replace any number with the number followed by a period (including the last digit!). This is saved into the variable `$num`. Since the last digit is followed by a period which we do not want, we remove it using bash's "remove suffix" expansion. – carlpett Aug 19 '11 at 11:24
  • Note: Had `sed` supported lookahead, the suffix removal would have been unneccesary with a small fix to the regex to not match the last number, something like `([0-9])(?!$)`. There would have to be some escaping so bash doesn't expand the !, too. – carlpett Aug 19 '11 at 11:26
  • Hm, acually, it doesn't work for me either when I wrap it up like this. Just `echo $S | sed -r "s/([0-9])/\1./g"` works though... I'll be back, as they say – carlpett Aug 19 '11 at 11:30
  • Oh cr... I'm stupid. Remove the spaces around the `=` and all should be fine. – carlpett Aug 19 '11 at 11:31
  • Thanks carlpett. I updated the question above with your tips. – Roger Aug 19 '11 at 12:23
2

I prefer also the sed for that:

echo 321 | sed -e 's/\([[:digit:]]\)/.\1/g' | cut -b2- -> 3.2.1

echo 32 | sed -e 's/\([[:digit:]]\)/.\1/g' | cut -b2- -> 3.2

Or without cut it looks like this

echo 321 | sed -e 's/\([[:digit:]]\)/.\1/g' -e 's/^\.//'
f4m8
  • 410
  • 2
  • 4
1
S='321'
perl -e "print join '.', split //, shift" "$S"
Blagovest Buyukliev
  • 42,498
  • 14
  • 94
  • 130