シェルスクリプトで文字列を配列に分解する

シェルスクリプトで文字列を配列に分解する

たとえば、文字列を配列に変換しようとしています。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
}

(入力にはロケールの charmap で適切にエンコードされたテキストが含まれていると想定されます)。


を用いると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 )

+ U+0301 グラフィムクラスターe(表示デバイスでは通常、éU+00E9 の合成済み同等文字と同じものを表す) は 2 つの文字 (U+0065 と U+0301) で構成され、charmap として UTF-8 を使用するロケールでは、最初の文字は 1 バイト (0x65) でエンコードされ、2 番目の文字は 2 バイト (0xcc 0x81、Meta-L および Meta-Ctrl-A とも呼ばれる) でエンコードされます。

のように ASCII 文字のみで構成された文字列の場合11111001、3 つすべてが同等になります。

zshksh/bash を除く他のすべてのシェルと同様に、配列インデックスは 0 ではなく 1 から始まることに注意してください。

答え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

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

関連情報