Dividir cadeia única em matriz de caracteres usando SOMENTE bash


9

Eu quero dividir 'hello'em h e l l oem uma matriz usando apenas bash, eu poderia fazê-lo em sed com sed 's/./& /g'mas eu quero saber como dividir uma string em um array em Bash quando eu não sei o que o delimitador seria, ou o delimitador é qualquer caractere único. Acho que não posso usar ${i// /}sem criatividade, porque o delimitador é desconhecido e não acho que essa expressão aceite regex. Eu tentei usar BASH_REMATCH com [[string = ~ ([az].). *]] Mas não funciona como eu esperava. Qual é a maneira correta de usar apenas o bash para realizar um string.split()tipo de comportamento? A razão é que eu estou tentando escrever o utilitário rev em todos os 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

Mas eu usei tr e sed, quero saber como fazer a divisão corretamente e depois vou consertar tudo. Apenas por diversão.


Deve haver duplicatas entre sites no estouro de pilha, dado o tamanho dele. Um candidato é Bash: Divide a string na matriz de caracteres .
22615 Peter Mortensen

[[ $string =~ ${string//?/(.)} ]]definido BASH_REMATCH[]como necessário, veja minha resposta à pergunta a que Peter Mortensen vincula para obter uma explicação.
precisa saber é o seguinte

Respostas:


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

Resultado:

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

ou (observe os comentários)

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

Resultado:

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

1
O segundo é bom, mas só funciona (nesse caso) porque você não cita "$ c"; portanto, reduz o espaço em branco e expande as estrelas em $ s.
rici

2
A segunda opção deve ser: while read -N 1 c; do a+=("$c"); done <<< "$s". O -Npermite ler até mesmo novas linhas, e as aspas ("$c")evitam a expansão do nome do caminho (e alguns outros problemas).

1
Claro, se a última nova linha deve ser corrigido, altere a linha dada a: while read -N 1 c; do a+=("$c"); done <<< "$s"xe, em seguida, apagar a última nova linha ea x acrescentou: unset a[${#a[@]}-1]; unset a[${#a[@]}-1].

4

Para dividir a string em uma matriz de caracteres, com delimitador nulo, você pode:

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

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

Com delimitador diferente de nulo, você pode:

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

Se IFSpode ser definido, então você pode apenas fazer: IFS=',' arr=($str).
Muni

2
@ muru: Você será preso com glob nesse caso. Atualizado com a desativação da glob.
precisa saber é

@BinaryZebra: Ah, sim, minha citação ruim está faltando. Corrigido, obrigado.
cuonglm

2

Apenas por diversão (e outras conchas) outra variante:

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

E verifique

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

irá imprimir

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

0

Método 1:

Oneliner:

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

Código expandido:

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:

Oneliner:

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

Código expandido:

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.

Graças a @cuonglm por sua sugestão.
Efetivamente, você pode usar $REPLYessa é a variável padrão onde readlê a entrada.


1
Você pode usar em $REPLYvez de charvariável.
precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.