0

My mate asked this before on the unix SE, but he asked it wrong. He didn't get a working answer either.

Anyway, I'm trying to make my bash script process each character in a variable and echo a certain string per letter until it reaches the last. Here's what I have so far:

#!/bin/bash

echo Word?
read -r -p '' foo
# $foo is set to 'Mammals and Bricks' by user.

wordlength=${#foo}
$wordlength says 18, so start on character 1.
'M' is first letter received in $foo, so echo '{m,M}'
'a' is second letter received in $foo, so echo '{a,A}'
'm' is third letter received in $foo, so echo '{m,M}'
'm' is fourth letter received in $foo, so echo '{m,M}'
'a' is the fifth letter received in $foo, so echo '{a,A}'
'l' is the sixth letter received in $foo, so echo '{l,L}'
's' is the seventh letter received in foo, so echo '{s,S}'
' ' is the eighth, so echo '\ '
........
'c' is sixteenth letter received in $foo, so echo '{c,C}'
'k' is seventeenth letter received in $foo, so echo '{k,K}'
's' is eighteenth letter received in $foo, so echo '{s,S}'

And here's what it would look like on the user's end:

Word?

Mammals and Bricks

{m,M}{a,A}{m,M}{m,M}{a,A}{l,L}{s,S} {a,A}{n,N}{d,D} {b,B}{r,R}{i,I}{c,C}{k,K}{s,S}

Which is what it would output exactly. You would see all of the above in raw characters.

Anyone know how to do this?

xhienne
  • 5,738
  • 1
  • 15
  • 34
leetbacoon
  • 1,111
  • 2
  • 9
  • 32

2 Answers2

2

Below is a solution with GNU sed, if you don't mind using it:

sed 's/[a-zA-Z]/{\l&,\u&}/g' <<< "$foo"

\l and \u are GNU extensions to sed that turn the next character to lowercase and uppercase, respectively.

[edit] And here is a solution with bash since you haven't GNU sed:

while read -r -n1; do
    if [[ "${REPLY^}" == [A-Z] ]]; then
        printf '{%c,%c}' "${REPLY,}" "${REPLY^}"
    else
        printf '%c' "$REPLY"
    fi
done <<< "$foo"
echo

[edit] P.S. Unfortunately, this won't work on OS X Yosemite as the ${var,} and ${var^} constructs were added in bash v4, but MacOS only ships with bash v3.2.57 (this is because bash v4 is licensed under GPL v3, which Apple doesn't want to comply with). Thanks to @GordonDavisson for adding this.

So here is a solution that should work with your bash v3:

printf '%s\n' "$foo" \
| while read -d '' -r -n1; do
    lowercase="$(printf '%c' "$REPLY" | tr '[:upper:]' '[:lower:]')"
    uppercase="$(printf '%c' "$REPLY" | tr '[:lower:]' '[:upper:]')"
    if [ "$lowercase" != "$uppercase" ]; then
        printf '{%c,%c}' "$lowercase" "$uppercase"
    else
        printf '%c' "$REPLY"
    fi
done
xhienne
  • 5,738
  • 1
  • 15
  • 34
  • Hmm... When I run that my output looks like this: `{lM,uM}{la,ua}{lm,um}{lm,um}{la,ua}{ll,ul}{ls,us} {la,ua}{ln,un}{ld,ud} {lB,uB}{lr,ur}{li,ui}{lc,uc}{lk,uk}{ls,us}`. I'm using OS X Yosemite btw – leetbacoon Jul 26 '17 at 22:56
  • Then your are probably using a classic sed, not GNU sed (note that GNU sed is probably available on OSX). I have added a solution in pure bash. – xhienne Jul 26 '17 at 23:03
  • 1
    @xhienne Unfortunately, the `${var,}` and `${var^}` constructs were added in bash v4, but macOS only ships with bash v3.2.57. (This is because bash v4 is licensed under GPL v3, which Apple doesn't want to comply with.) – Gordon Davisson Jul 27 '17 at 05:18
  • @GordonDavisson Thank you for your interesting comment. I will amend my answer. – xhienne Jul 27 '17 at 08:27
  • Well, since doing it this way isn't possible through Yosemite's Terminal, are there any other ways to do this? – leetbacoon Jul 30 '17 at 16:29
  • Thanks, @xhienne, but I tried that and got errors saying `line 2: declare: -l: invalid option` and `line 3: declare: -u: invalid option`. And then it quits. – leetbacoon Aug 20 '17 at 00:33
  • @leetbacoon Edited my answer. It should work now with your version of bash. – xhienne Aug 20 '17 at 15:03
1

Start by looping through the characters in your string:

foo=string
for (( i=0; i<${#foo}; i++ )); do
  echo "${foo:$i:1}"
done

( Reference: How to perform a for loop on each character in a string in BASH? )

Now replace the echo statement with a case statement:

foo=string
for (( i=0; i<${#foo}; i++ )); do
  case "${foo:$i:1}" in
    a)
        echo "Do something with a here";
        ;;
    [bB])
        echo "Do something with b or B here";
        ;;
   esac
done
Ty Savercool
  • 1,132
  • 5
  • 10