![Разбить строку на массив в скрипте оболочки](https://rvso.com/image/192230/%D0%A0%D0%B0%D0%B7%D0%B1%D0%B8%D1%82%D1%8C%20%D1%81%D1%82%D1%80%D0%BE%D0%BA%D1%83%20%D0%BD%D0%B0%20%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%20%D0%B2%20%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B5%20%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B8.png)
Я пытаюсь преобразовать строку, например, 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 grep
3.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")