Разбить строку на массив в скрипте оболочки

Разбить строку на массив в скрипте оболочки

Я пытаюсь преобразовать строку, например, string=11111001в массив, к которому я смогу получить доступ, вызвав соответствующий индекс массива, например

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

Я новичок в написании сценариев оболочки, и из того, что я прочитал, следует, что в нем нет разделителя, и я застрял.

Кто-нибудь может мне помочь?

решение1

bashуже есть форма этого посредством нарезки строк:

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

Синтаксис для этого — ${variable:start:length}, и вернет следующееlengthсимволы, начиная сstartСимвол ᵗʰ (с нулевым индексом).

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

решение2

Для полноты картины, с помощью zsh, разбить строку на:

егохарактерСостав:

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

(если $stringсодержит байты, не являющиеся частями допустимых символов, каждый из них все равно будет сохранен как отдельный элемент)

егобайтсоставляющие

вы можете сделать то же самое, но после того, как отключитемногобайтовыйвариант, например локально в анонимной функции:

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

егокластер графемсоставляющие.

Вы можете использовать возможности PCRE для их сопоставления с \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
}

(предполагается, что входные данные содержат текст, правильно закодированный в таблице символов локали).


С string=$'Ste\u0301phane', они дают:

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 )

Так как eкластер графем + U+0301 (который устройства отображения обычно представляют так же, как и éпредварительно составленный эквивалент U+00E9) состоит из 2 символов (U+0065 и U+0301), то в локалях, использующих UTF-8 в качестве своей кодировки, первый из них кодируется одним байтом (0x65), а второй — двумя байтами (0xcc 0x81, также известный как Meta-L и Meta-Ctrl-A).

Для строк, состоящих только из символов ASCII, таких как ваш 11111001, все три будут эквивалентны.

Обратите внимание, что, zshкак и во всех других оболочках, за исключением ksh/bash, индексы массивов начинаются с 1, а не с 0.

решение3

Вы можете разбить строку на отдельные символы:

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

и прочитать их обратно как массив:

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

Тогда, конечно, каждый символ будет находиться в каждом индексе массива arr.

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

Но зачем создавать новый массив, если bash может получить доступ к каждому отдельному символу напрямую, как показано ниже:

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

Подробнее читайте ${parameter:offset:length}в man bash.

решение4

Начиная с bashверсии 4.4+, поскольку она bashв любом случае не может хранить символы NUL в своих переменных, вы можете вызвать другую утилиту для выполнения разделения и вывода результата с разделителями NUL, который вы можете прочитать в массив с помощью readarray -td ''.

Если ваша система поставляется с реализацией GNU grep, вы можете сделать следующее:

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

Все, кроме первого, будут пропускать байты, которые не являются частью допустимых символов в локали (по крайней мере, с GNU grep3.4). Например, с string=$'Ste\u0301phane \\\xf0\x80z.'(завершающая часть не является допустимым UTF-8), в локали UTF-8, что дает:

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

Если вы не используете систему GNU и предполагаете, что $stringсодержит допустимый текст UTF-8, вы можете использовать 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")

Связанный контент