Dividir una sola cadena en una matriz de caracteres usando SOLO bash

9

Quiero dividir 'hello'en h e l l oen una matriz utilizando sólo golpe, que podría hacerlo en sed con sed 's/./& /g'pero quiero saber cómo dividir una cadena en una matriz en Bash cuando no sé lo que sería el delimitador o el delimitador es cualquier solo personaje No creo que pueda usar ${i// /}sin un poco de creatividad porque el delimitador es un desconocido, y no creo que esa expresión acepte expresiones regulares. Intenté usar BASH_REMATCH con [[string = ~ ([az].). *]] Pero no funciona como esperaba. ¿Cuál es la forma correcta de usar solo bash para lograr un string.split()tipo de comportamiento? La razón es que estoy tratando de escribir la utilidad rev en todo 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

Pero usé tr y sed, quiero saber cómo hacer la división correctamente y luego lo arreglaré para que sea todo bash. Solo por diversión.

Gregg Leventhal
fuente
Debe haber duplicados entre sitios en Stack Overflow, dado su tamaño. Un candidato es Bash: dividir la cadena en una matriz de caracteres .
Peter Mortensen
[[ $string =~ ${string//?/(.)} ]]establecerá BASH_REMATCH[]según sea necesario, vea mi respuesta a la pregunta a la que Peter Mortensen se vincula para obtener una explicación.
Sr.Spuratic

Respuestas:

12
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

Salida:

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

o (tenga en cuenta los comentarios)

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

Salida:

declare -aa = '([0] = "h" [1] = "e" [2] = "l" [3] = "l" [4] = "o")'
Ciro
fuente
1
El segundo es bueno, pero solo funciona (en este caso) porque no cita "$ c", por lo que dejará espacios en blanco y expandirá las estrellas en $ s.
rici
2
La segunda opción debe ser: while read -N 1 c; do a+=("$c"); done <<< "$s". El -Npermite leer incluso los saltos de línea, y las cotizaciones de ("$c")evitar la expansión ruta (y algunas otras cuestiones).
1
Por supuesto, si el último salto de línea se debe corregir, cambiar la línea dada a: while read -N 1 c; do a+=("$c"); done <<< "$s"xy luego borrar el último salto de línea y el x añadido: unset a[${#a[@]}-1]; unset a[${#a[@]}-1].
4

Para dividir la cadena en una matriz de caracteres, con delimitador nulo, puede:

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

printf '%s\n' "${arr[@]}"

Con un delimitador que no sea nulo, puede:

set -f
str='1,2,3,4,5'
IFS=',' arr=($str)
printf '%s\n' "${arr[@]}"
Cuonglm
fuente
Si IFSse puede establecer, a continuación, puedes hacer: IFS=',' arr=($str).
muru
2
@muru: En ese caso, te quedarás atrapado con glob. Actualizado con deshabilitante glob.
Cuonglm
@BinaryZebra: Ah, sí, mi mala cita faltante. Lo arreglé, gracias.
Cuonglm
2

Solo por diversión (y otras conchas) otra variante:

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

Y comprobar

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

imprimirá

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

Método 1:

Un trazador de líneas:

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

Código ampliado:

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.

Método 2:

Un trazador de líneas:

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

Código ampliado:

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.

Gracias a @cuonglm por su sugerencia.
Efectivamente, puede usar $REPLYesa es la variable predeterminada donde readlee la entrada.

Helio
fuente
1
Puede usar en $REPLYlugar de charvariable.
Cuonglm