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

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

文字列を配列に変換しようとしています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ᵗʰ文字(0インデックス)。

$ 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を使用します。最初のものは1バイト(0x65)でエンコードされ、2番目は2バイト(0xcc 0x81、Meta-L、Meta-Ctrl-Aとも呼ばれます)でエンコードされます。

ASCII文字(たとえば、あなたの文字)で構成される文字列の場合、11111001これら3文字は同じです。

ksh / bashを除く他のすべてのシェルと同様に、配列インデックスはzshゼロではなく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")

関連情報