Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

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.

share|improve this question
    
There must be cross-site duplicates on Stack Overflow, given the size of it. One candidate is Bash: Split string into character array. –  Peter Mortensen Apr 19 at 22:34

4 Answers 4

up vote 5 down vote accepted
s="hello"
declare -a a
for ((i=0; i<${#s}; i++)); do a[$i]="${s:$i:1}"; done
declare -p a

Output:

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

or

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")'
share|improve this answer
    
The second one is nice but it only works (in this case) because you don't quote "$c", so it will drop whitespace and expand stars in $s. –  rici Apr 20 at 3:14
    
This: for ((i=0; i<${#s}; i++)); do a[i]="${s:i:1}"; done also work, The $ are not needed inside arithmetic evaluation (which in the index [ ] and in the : : are). –  BinaryZebra Aug 16 at 23:01
    
The second option should be: while read -N 1 c; do a+=("$c"); done <<< "$s". The -N allows to read even newlines, and the quotes ("$c") avoid pathname expansion (and some other issues). –  BinaryZebra Aug 16 at 23:05
    
Of course, if the last newline must be corrected, change the line given to: while read -N 1 c; do a+=("$c"); done <<< "$s"x and then erase the last newline and the x added: unset a[${#a[@]}-1]; unset a[${#a[@]}-1]. –  BinaryZebra Aug 16 at 23:20

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 '%s\n' "${arr[@]}"

With delimiter other than null, you can:

set -f
str='1,2,3,4,5'
IFS=',' arr=($str)
printf '%s\n' "${arr[@]}"
share|improve this answer
    
If IFS can be set, then you can just do: IFS=',' arr=($str). –  muru Apr 19 at 16:46
2  
@muru: You will be stucked with glob in that case. Updated with disabling glob. –  cuonglm Apr 19 at 16:50
    
The first option breaks on *. Try str='he*llo'. Solved by doing: arr+=("${str:$i:1}"). The second option breaks if $str contains newlines. –  BinaryZebra Aug 16 at 18:52
    
@BinaryZebra: Ah, yes, my bad, missing quote. Fixed it, thanks. –  cuonglm Aug 17 at 1:35

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
share|improve this answer

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.

share|improve this answer
1  
You can use $REPLY instead of char variable. –  cuonglm Apr 19 at 17:01
    
@cuonglm: Thanks! –  Helio Apr 19 at 17:17

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.