Divida a string em array no shell script

Divida a string em array no shell script

Estou tentando converter uma string, por exemplo, string=11111001em um array que poderei acessar chamando o respectivo índice do array, como

arr[0]=1, arr[1]=0

Sou novo em scripts de shell e pelo que li, ele não tem um separador, estou travado.

Alguém pode me ajudar?

Responder1

bashjá tem uma forma disso por meio de fatiamento de string:

$ word="word"
$ printf "%s\n" "${word:0:1}"
w
$ printf "%s\n" "${word:1:1}"
o

A sintaxe para isso é ${variable:start:length}e retornará o próximolengthpersonagens começando nostartCaractere ᵗʰ (indexado em zero).

$ printf "%s\n" "${word:2:2}"
rd

Responder2

Para completar, com zsh, para dividir uma string em:

isso épersonagemconstituintes:

chars=( ${(s[])string} )

(se $stringcontiver bytes que não façam parte de caracteres válidos, cada um deles ainda será armazenado como elementos separados)

isso ébyteconstituintes

você pode fazer o mesmo, mas depois de desativar omultibyteopção, por exemplo localmente em uma função anônima:

(){ set -o localoptions +o multibyte
  bytes=( ${(s[])string} )
}

isso écluster de grafemaconstituintes.

Você pode usar a capacidade do PCRE para combiná-los com \X:

zmodload zsh/pcre
(){
  graphemes=()
  local rest=$string match
  pcre_compile -s '(\X)\K.*'
  while pcre_match -v rest -- "$rest"; do
    graphemes+=($match[1])
  done
}

(isso pressupõe que a entrada contém texto devidamente codificado no charmap do local).


Com string=$'Ste\u0301phane', aqueles dão:

chars=( S t e ́ p h a n e )
bytes=( S t e $'\M-L' $'\M-\C-A' p h a n e )
graphemes=( S t é p h a n e )

Como o ecluster de grafema + U+0301 (cujos dispositivos de exibição geralmente representam o mesmo que o éequivalente pré-composto U+00E9) é composto de 2 caracteres (U+0065 e U+0301) onde em localidades usando UTF-8 como seu charmap, o primeiro é codificado em um byte (0x65) e o segundo em dois bytes (0xcc 0x81, também conhecido como Meta-L e Meta-Ctrl-A).

Para strings compostas apenas por caracteres ASCII como your 11111001, todos os três serão equivalentes.

Observe que, zshcomo em todos os outros shells, exceto ksh/bash, os índices do array começam em 1, não em 0.

Responder3

Você pode dividir a string em caracteres individuais:

string=11111001
echo "$string" | grep -o .

e leia-os de volta como uma matriz:

readarray -t arr <<<"$(grep -o . <<<"$string")"

Então, é claro, cada caractere estaria em cada índice do arrarray.

$ declare -p arr
declare -a arr=([0]="1" [1]="1" [2]="1" [3]="1" [4]="1" [5]="0" [6]="0" [7]="1")

Mas por que criar um novo array se o bash pudesse acessar cada caractere individual diretamente assim:

$ string=11111001
echo "${string:5:1}" "${string:7:1}"
0 1

Leia sobre ${parameter:offset:length}em man bash.

Responder4

Com bash4.4+, como bashnão é possível armazenar caracteres NUL em suas variáveis, você pode chamar um utilitário diferente para fazer a divisão e imprimir o resultado delimitado por NUL, que pode ser lido em um array com readarray -td ''.

Se o seu sistema vier com a implementação GNU do grep, você poderia fazer:

readarray -td '' bytes < <(printf %s "$string" | LC_ALL=C grep -zo .)
readarray -td '' chars < <(printf %s "$string" | grep -zo .)
readarray -td '' graphemes < <(printf %s "$string" | grep -zPo '\X')

Todos, exceto o primeiro, irão pular bytes que não fazem parte de caracteres válidos no código do idioma (pelo menos com GNU grep3.4). Por exemplo, com string=$'Ste\u0301phane \\\xf0\x80z.'(a parte final não formando UTF-8 válido), em um código de idioma UTF-8, isso fornece:

declare -a bytes=([0]="S" [1]="t" [2]="e" [3]=$'\314' [4]=$'\201' [5]="p" [6]="h" [7]="a" [8]="n" [9]="e" [10]=" " [11]="\\" [12]=$'\360' [13]=$'\200' [14]="z" [15]=".")
declare -a chars=([0]="S" [1]="t" [2]="e" [3]="́" [4]="p" [5]="h" [6]="a" [7]="n" [8]="e" [9]=" " [10]="\\" [11]="z" [12]=".")
declare -a graphemes=([0]="S" [1]="t" [2]="é" [3]="p" [4]="h" [5]="a" [6]="n" [7]="e" [8]=" " [9]="\\" [10]="z" [11]=".")

Se não estiver em um sistema GNU, e assumindo que $stringcontém texto UTF-8 válido, você poderia usar perl:

readarray -td '' bytes < <(perl -0le 'print for split "", shift' -- "$string")
readarray -td '' chars < <(perl -CSA -0le 'print for split "", shift' -- "$string")
readarray -td '' graphemes < <(perl -CSA -0le 'print for shift =~ /\X/g' -- "$string")

informação relacionada