Divida la cadena en una matriz en el script de shell

Divida la cadena en una matriz en el script de shell

Estoy intentando convertir una cadena, por ejemplo, string=11111001en una matriz a la que podré acceder llamando al índice de matriz respectivo como

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

Soy nuevo en los scripts de shell y, por lo que leí, no tiene separador. Estoy atascado.

¿Alguien me puede ayudar?

Respuesta1

bashya tiene una forma de esto mediante corte de cuerdas:

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

La sintaxis para esto es ${variable:start:length}y devolverá el siguientelengthpersonajes comenzando en elstartCarácter ᵗʰ (indexado a cero).

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

Respuesta2

Para completar, con zsh, para dividir una cadena en:

espersonajeconstituyentes:

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

(Si $stringcontiene bytes que no forman parte de caracteres válidos, cada uno de ellos se almacenará como elementos separados)

esbyteconstituyentes

puedes hacer lo mismo pero después de haber desarmado elmultibyteopción, por ejemplo localmente en una función anónima:

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

esgrupo de grafemasconstituyentes.

Puede utilizar la capacidad de PCRE para combinarlos con \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
}

(se supone que la entrada contiene texto correctamente codificado en el mapa de encanto de la configuración regional).


Con string=$'Ste\u0301phane', esos dan:

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 el egrupo de grafemas + U+0301 (que los dispositivos de visualización generalmente representan lo mismo que el éequivalente precompuesto U+00E9) se compone de 2 caracteres (U+0065 y U+0301), donde en las configuraciones regionales que usan UTF-8 como su mapa de encanto, el primero está codificado en un byte (0x65) y el segundo en dos bytes (0xcc 0x81, también conocido como Meta-L y Meta-Ctrl-A).

Para cadenas formadas únicamente por caracteres ASCII como su 11111001, los tres serán equivalentes.

Tenga en cuenta que, zshal igual que en todos los demás shells excepto ksh/bash, los índices de matriz comienzan en 1, no en 0.

Respuesta3

Podrías dividir la cadena en caracteres individuales:

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

y leerlos como una matriz:

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

Entonces, por supuesto, cada carácter estaría en cada índice de la arrmatriz.

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

Pero, ¿por qué crear una nueva matriz si bash puede acceder a cada carácter individual directamente de esta manera?

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

Lea sobre ${parameter:offset:length}en man bash.

Respuesta4

Con bash4.4+, como bashde todos modos no se pueden almacenar caracteres NUL en sus variables, se puede llamar a una utilidad diferente para realizar la división e imprimir el resultado delimitado por NUL, que se puede leer en una matriz con readarray -td ''.

Si su sistema viene con la implementación GNU de grep, podría hacer:

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 menos el primero omitirán bytes que no formen parte de caracteres válidos en la configuración regional (al menos con GNU grep3.4). Por ejemplo, con string=$'Ste\u0301phane \\\xf0\x80z.'(la parte final no forma UTF-8 válido), en una configuración regional UTF-8, eso da:

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]=".")

Si no está en un sistema GNU, y suponiendo que $stringcontenga texto UTF-8 válido, podría usar perlen su lugar:

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")

información relacionada