Split single string into character array using ONLY bash

I want to split 'hello' into h e l l o in an array using only bash, I could do it in sed with sed 's/./& /g' but I want to know how to split a string into an array in Bash when I do not know what the delimiter would be, or the delimiter is any single character. I don’t think I can use ${i// /} without some creativity because the delimiter is an unknown, and I don’t think that expression accepts regex. I tried using BASH_REMATCH with [[ string =~ ([a-z].).* ]] but it doesn’t work as I expected. What is the proper way to use only bash to accomplish a string.split() type of behavior? The reason is that I am trying to write the rev utility in all bash:

  while read data; do
  word=($(echo $data|tr ' ' '_'|sed 's/./& /g'))
  new=()
  i=$((${#word[@]} - 1))
  while [[ $i -ge 0 ]]; do
    new+=(${word[$i]})
    (( i-- ))
  done
  echo ${new[@]}|tr -d ' '|tr '_' ' '
  done

But I used tr and sed, I want to know how to do the split properly and then I will fix it to be all bash. Just for fun.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

s="hello"
declare -a a   # define array a
for ((i=0; i<${#s}; i++)); do a[$i]="${s:$i:1}"; done
declare -p a   # print array a in a reusable form

Output:

declare -a a='([0]="h" [1]="e" [2]="l" [3]="l" [4]="o")'

or (please note the comments)

s="hello"
while read -n 1 c; do a+=($c); done  <<< "$s"
declare -p a

Output:

declare -a a='([0]="h" [1]="e" [2]="l" [3]="l" [4]="o")'

Method 2

To split string into array of characters, with null delimiter, you can:

str='hello'
arr=()
i=0
while [ "$i" -lt "${#str}" ]; do
  arr+=("${str:$i:1}")
  i=$((i+1))
done

printf '%sn' "${arr[@]}"

With delimiter other than null, you can:

set -f
str='1,2,3,4,5'
IFS=',' arr=($str)
printf '%sn' "${arr[@]}"

Method 3

Just for fun (and other shells) other variant:

word=hello
unset letter
while [ ${#word} -gt 0 ]
do
    rest=${word#?}
    letter[${#letter[*]}]=${word%$rest}
    word=$rest
done

And check

for l in "${!letter[@]}"
do
    echo "letter [$l] = ${letter[l]}"
done

will print

letter [0] = h
letter [1] = e
letter [2] = l
letter [3] = l
letter [4] = o

Method 4

Method 1:

Oneliner:

s="hello"
for ((i=0;i<${#s};i++)); do result[$i]="${s:i:1}"; done
echo ${result[@]}

Expanded code:

s="hello"                   # Original string.

for ((i=0;i<${#s};i++)); do # For i=0; i<(length of s); i++
    result[$i]="${s:i:1}"       # result[i] is equal to the i th character of $s
done                        # End of the loop

echo ${result[@]} # Print all elements of $result.

Method 2:

Oneliner:

s="hello"
var=($(while read -n 1; do printf "$REPLY "; done <<< "$s"))
echo "${var[@]}"

Expanded code:

s="hello" # Original string.

while read -n 1; do # Read chraracter by character the string.
    space_separated=$(printf "$space_separated $REPLY") # $space_separated is equal to it plus the current character.
done <<< "$s" # Pass the string to the loop

result=($space_separated) # Split $space_separated into an array.

echo "${result[@]}" # Print all elements of $result.

Thanks to @cuonglm by it’s suggestion.
Effectively, you can use $REPLY that is the default varible where read reads the input.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x