web-dev-qa-db-ja.com

途中のインデックスでbash配列をシフトするにはどうすればよいですか?

1  #!/bin/bash
2  # query2.sh
3
4  numbers=(53 8 12 9 784 69 8 7 1)
5  i=4
6
7  echo ${numbers[@]} # <--- this echoes "53 8 12 9 784 69 8 7 1" to stdout.
8  echo ${numbers[i]} # <--- this echoes "784" to stdout.
9
10 unset numbers[i]
11
12 echo ${numbers[@]} # <--- this echoes "53 8 12 9 69 8 7 1" to stdout.
13 echo ${numbers[i]} # <--- stdout is blank.

13行目でstdoutが空白になっているのはなぜですか。12行目のstdoutで配列が更新されているように見えるのを考えてみてください。

したがって、意図された回答「69」を取得するにはどうすればよいですか?

12
Anthony Webber

unsetは要素を削除します。残りの要素の番号は付け直しません。

declare -pを使用して、numbersがどうなるかを正確に確認できます。

$ unset "numbers[i]"
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

numbersに要素4がなくなったことを確認します。

もう一つの例

観察する:

$ a=()
$ a[1]="element 1"
$ a[22]="element 22"
$ declare -p a
declare -a a=([1]="element 1" [22]="element 22")

配列aには2から21までの要素がありません。Bashでは、配列のインデックスが連続している必要はありません。

インデックスの再番号付けを強制する推奨方法

要素4のないnumbers配列から始めましょう:

$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

インデックスを変更する場合は、次のようにします。

$ numbers=("${numbers[@]}")
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [4]="69" [5]="8" [6]="7" [7]="1")

要素番号4があり、値は69です。

1つの手順で要素を削除して配列の番号を付け直す別の方法

もう一度、numbersを定義しましょう:

$ numbers=(53 8 12 9 784 69 8 7 1)

コメントの Toby Speight で示唆されているように、4番目の要素を削除し、残りの要素の番号を1つのステップですべて再設定する方法:

$ numbers=("${numbers[@]:0:4}" "${numbers[@]:5}")
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [4]="69" [5]="8" [6]="7" [7]="1")

ご覧のとおり、4番目の要素が削除され、残りのすべての要素の番号が付け直されました。

${numbers[@]:0:4}スライス配列numbers:要素0から始まる最初の4つの要素を取得します。

同様に、${numbers[@]:5}スライス配列numbers:要素5で始まり、配列の最後まで続くすべての要素を取ります。

配列のインデックスを取得する

配列のvalues${a[@]}で取得できます。これらの値に対応するindices(またはkeys)を見つけるには、${!a[@]}を使用します。

たとえば、要素4のない配列numbersをもう一度考えてみます。

$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

割り当てられているインデックスを確認するには:

$ echo "${!numbers[@]}"
0 1 2 3 5 6 7 8

繰り返しますが、4はインデックスのリストにありません。

ドキュメンテーション

man bashから:

unsetビルトインは、配列を破棄するために使用されます。 unset name[subscript]は、インデックスsubscriptの配列要素を破棄します。インデックス付き配列への負の添え字は、上記のように解釈されます。パス名の展開による不要な副作用を回避するように注意する必要があります。 unset name、ここでnameは配列、またはunset name[subscript]、ここでsubscript*または@は、アレイ全体。

21
John1024

bashのようなksh配列は実際には配列ではなく、正の整数に制限されたキーを持つ連想配列(またはいわゆるスパース配列)に似ています。実際の配列を持つシェルの場合、rcesfishyashzsh(またはcsh/tcshですが、これらのシェルには非常に多くの問題があるため、回避することをお勧めします)。

zsh内:

a=(1 2 3 4 5)
a[3]=() # remove the 3rd element
a[1,3]=() # remove the first 3 elements
a[-1]=() # remove the last element

(zshでは、unset 'a[3]'は実際にkshとの互換性を高めるために空の文字列に設定します)

yash

a=(1 2 3 4 5)
array -d a 3 # remove the 3rd element
array -d a 1 2 3 # remove the first 3 elements
array -d a -1 # remove the last element

fishbash/zshに反するボーンのようなシェルではない):

set a 1 2 3 4 5
set -e a[3] # remove the 3rd element
set -e a[1..3] # remove the first 3 elements
set -e a[-1] # remove the last element

es内(rcに基づく、ボーンのようなものではない)

a = 1 2 3 4 5
a = $a(... 2 4 ...) # remove the 3rd element
a = $a(4 ...) # remove the first 3 elements
a = $a(... `{expr $#a - 1}) # remove the last element
# or a convoluted way that avoids forking expr:
a = $a(... <={@{*=$*(2 ...); return $#*} $a})

kshおよびbash

次の場合、配列を通常の配列として使用できます。

a=("${a[@]}")

インデックスのリストが連続していないか、0で始まらない可能性がある各削除または挿入操作の後。ksh/bash配列は1ではなく0で始まることに注意してください($@(いくつかの方法で))。

これにより、要素が整理され、インデックス0、1、2 ...に順に移動します。

また、number[i]を引用符で囲む必要があることに注意してください。

unset 'number[i]'

それ以外の場合は、現在のディレクトリにnumberiというファイルがあった場合、unset numberiとして扱われます。

5