62

Linuxシステムの一部を管理するスクリプトを計画しており、のどちらを使用するかを決定する段階にあります。

コマンドが簡単であるという理由だけで、これをBashスクリプトとして実行したいと思いますが、実際の決定要因は構成です。スクリプトにそれ自体をどう処理するかを指示するために、構成ファイルに多次元配列を格納できる必要があります。単純なキーと値のペアを構成ファイルに格納するのはbashを使用すれば簡単ですが、多次元配列を実行するために考えられる唯一の方法は、次のような2層の解析エンジンです。

array=&d1|v1;v2;v3&d2|v1;v2;v3

しかし、マーシャル/アンマーシャルのコードはクマになる可能性があり、これを管理しなければならない次の貧弱な樹液にとってはユーザーフレンドリーとはほど遠いものです。これをbashで簡単に実行できない場合は、構成をxmlファイルに書き込み、スクリプトをpythonで記述します。

これをbashで行う簡単な方法はありますか?

みんな、ありがとう。

4

13 に答える 13

56

Bashは多次元配列もハッシュもサポートしておらず、値が配列であるハッシュが必要なようです。このソリューションはあまり美しくありません。xmlファイルを使用したソリューションの方が優れているはずです。

array=('d1=(v1 v2 v3)' 'd2=(v1 v2 v3)')
for elt in "${array[@]}";do eval $elt;done
echo "d1 ${#d1[@]} ${d1[@]}"
echo "d2 ${#d2[@]} ${d2[@]}"

編集:bash 4はハッシュテーブルをサポートしているため、この回答はかなり古いものです。evalを使用しないソリューションについては、この回答も参照してください。

于 2012-06-27T20:13:37.773 に答える
29

Bashには多次元配列がありません。ただし、連想配列を使用すると、多少似た効果をシミュレートできます。以下は、多次元配列として使用されるふりをしている連想配列の例です。

declare -A arr
arr[0,0]=0
arr[0,1]=1
arr[1,0]=2
arr[1,1]=3
echo "${arr[0,0]} ${arr[0,1]}" # will print 0 1

配列を(と-A)連想として宣言しない場合、上記は機能しません。たとえば、declare -A arr行を省略すると、の代わりにechoが出力されます。これは、などが算術式と見なされ、(コンマ演算子の右側の値)に評価されるためです。2 30 10,01,00

于 2015-07-17T05:27:46.077 に答える
21

これは、1。の「間接拡張」のおかげで機能します。これにより、間接参照の1つのレイヤーが追加されます。2。配列とは異なる動作をし、 https://stackoverflow.com/a/1336245/317623で説明されているように配列を「スライス」するために使用できる「サブストリング拡張」

# Define each array and then add it to the main one
SUB_0=("name0" "value 0")
SUB_1=("name1" "value;1")
MAIN_ARRAY=(
  SUB_0[@]
  SUB_1[@]
)

# Loop and print it.  Using offset and length to extract values
COUNT=${#MAIN_ARRAY[@]}
for ((i=0; i<$COUNT; i++))
do
  NAME=${!MAIN_ARRAY[i]:0:1}
  VALUE=${!MAIN_ARRAY[i]:1:1}
  echo "NAME ${NAME}"
  echo "VALUE ${VALUE}"
done

ここでのこの回答に基づいています

于 2015-02-03T20:32:39.437 に答える
10

bashスクリプトを使用して読みやすくしたい場合は、データを構造化JSONに配置してから、bashコマンドで軽量ツールjqを使用して配列を反復処理することをお勧めします。たとえば、次のデータセットを使用します。

[

    {"specialId":"123",
    "specialName":"First"},

    {"specialId":"456",
    "specialName":"Second"},

    {"specialId":"789",
    "specialName":"Third"}
]

次のように、bashスクリプトとjqを使用してこのデータを反復処理できます。

function loopOverArray(){

    jq -c '.[]' testing.json | while read i; do
        # Do stuff here
        echo "$i"
    done
}

loopOverArray

出力:

{"specialId":"123","specialName":"First"}
{"specialId":"456","specialName":"Second"}
{"specialId":"789","specialName":"Third"}
于 2019-06-15T22:28:47.247 に答える
8

使用されているシェル(sh、ksh、bashなど)に関係なく、次のアプローチはn次元配列に対して非常にうまく機能します(サンプルは2次元配列をカバーしています)。

サンプルでは、​​行区切り文字(1次元)はスペース文字です。フィールドセパレータ(2次元)を導入するには、標準のUNIXツールtrを使用します。追加の寸法用の追加のセパレーターも同じ方法で使用できます。

もちろん、このアプローチのパフォーマンスはあまり良くありませんが、パフォーマンスが基準でない場合、このアプローチは非常に一般的であり、多くの問題を解決できます。

array2d="1.1:1.2:1.3 2.1:2.2 3.1:3.2:3.3:3.4"

function process2ndDimension {
    for dimension2 in $*
    do
        echo -n $dimension2 "   "
    done
    echo
}

function process1stDimension {
    for dimension1 in $array2d
    do
        process2ndDimension `echo $dimension1 | tr : " "`
    done
}

process1stDimension

そのサンプルの出力は次のようになります。

1.1     1.2     1.3     
2.1     2.2     
3.1     3.2     3.3     3.4 
于 2013-10-05T00:42:45.917 に答える
5

多くの試行錯誤の末、私は実際にbashで最良で、最も明確で、最も簡単な多次元配列を見つけるのは、通常の変数を使用することです。うん。

利点:大きな配列をループする必要はありません。「$ var」をエコーし​​て、grep / awk/sedを使用できます。簡単で明確で、好きなだけ列を作成できます。

例:

$ var=$(echo -e 'kris hansen oslo\nthomas jonson peru\nbibi abu johnsonville\njohnny lipp peru')

$ echo "$var"
kris hansen oslo
thomas johnson peru
bibi abu johnsonville
johnny lipp peru

ペルーのみんなを見つけたいなら

$ echo "$var" | grep peru
thomas johnson peru
johnny lipp peru

3番目のフィールドのgrep(sed)のみ

$ echo "$var" | sed -n -E '/(.+) (.+) peru/p'
thomas johnson peru
johnny lipp peru

xフィールドのみが必要な場合

$ echo "$var" | awk '{print $2}'
hansen
johnson
abu
johnny

トーマスと呼ばれるペルーの誰もが彼の姓を返す

$ echo "$var" |grep peru|grep thomas|awk '{print $2}'
johnson

あなたが考えることができるどんな質問でも...超簡単。

アイテムを変更するには:

$ var=$(echo "$var"|sed "s/thomas/pete/")

「x」を含む行を削除するには

$ var=$(echo "$var"|sed "/thomas/d")

別のアイテムの値に基づいて同じ行の別のフィールドを変更するには

$ var=$(echo "$var"|sed -E "s/(thomas) (.+) (.+)/\1 test \3/")
$ echo "$var"
kris hansen oslo                                                                                                                                             
thomas test peru                                                                                                                                          
bibi abu johnsonville
johnny lipp peru

もちろん、それをやりたいのであれば、ループも機能します

$ for i in "$var"; do echo "$i"; done
kris hansen oslo
thomas jonson peru
bibi abu johnsonville
johnny lipp peru

これで見つかった唯一の落とし穴は、常にvar(例ではvarとiの両方)を引用する必要があるということです。そうしないと、次のようになります。

$ for i in "$var"; do echo $i; done
kris hansen oslo thomas jonson peru bibi abu johnsonville johnny lipp peru

入力にスペースがあると機能しないと誰かが言うことは間違いありませんが、入力に別の区切り文字を使用することで修正できます。たとえば、(utf8文字を使用して、入力では選択できないものを選択できることを強調します)。含むが、あなたは何でも選ぶことができるc):

$ var=$(echo -e 'field one☥field two hello☥field three yes moin\nfield 1☥field 2☥field 3 dsdds aq')

$ for i in "$var"; do echo "$i"; done
field one☥field two hello☥field three yes moin
field 1☥field 2☥field 3 dsdds aq

$ echo "$var" | awk -F '☥' '{print $3}'
field three yes moin
field 3 dsdds aq

$ var=$(echo "$var"|sed -E "s/(field one)☥(.+)☥(.+)/\1☥test☥\3/")
$ echo "$var"
field one☥test☥field three yes moin
field 1☥field 2☥field 3 dsdds aq

入力に改行を格納する場合は、入力の前に改行を別の行に変換し、出力時に再度変換することができます(またはbashを使用しないでください...)。楽しみ!

于 2017-04-25T12:42:31.870 に答える
4

これは、Bashの2次元配列の動作を(少なくともある程度は)模倣するための非常に単純で明確な方法であるため、以下を投稿します。ヒアファイル(Bashのマニュアルを参照)とread(Bashの組み込みコマンド)を使用します。

## Store the "two-dimensional data" in a file ($$ is just the process ID of the shell, to make sure the filename is unique)
cat > physicists.$$ <<EOF
Wolfgang Pauli 1900
Werner Heisenberg 1901
Albert Einstein 1879
Niels Bohr 1885
EOF
nbPhysicists=$(wc -l physicists.$$ | cut -sf 1 -d ' ')     # Number of lines of the here-file specifying the physicists.

## Extract the needed data
declare -a person     # Create an indexed array (necessary for the read command).                                                                                 
while read -ra person; do
    firstName=${person[0]}
    familyName=${person[1]}
    birthYear=${person[2]}
    echo "Physicist ${firstName} ${familyName} was born in ${birthYear}"
    # Do whatever you need with data
done < physicists.$$

## Remove the temporary file
rm physicists.$$

出力: Physicist Wolfgang Pauli was born in 1900 Physicist Werner Heisenberg was born in 1901 Physicist Albert Einstein was born in 1879 Physicist Niels Bohr was born in 1885

それが機能する方法:

  • 作成された一時ファイルの行は、1次元ベクトルの役割を果たします。ここで、空白スペース(または選択した分離文字read。Bashマニュアルのコマンドの説明を参照)がこれらのベクトルの要素を分離します。
  • 次に、readコマンドとその-aオプションを使用して、ファイルの各行をループします(ファイルの終わりに達するまで)。行ごとに、ループの直前に宣言した配列に目的のフィールド(=単語)を割り当てることができます。コマンドの-rオプションreadは、ヒアドキュメントに円記号を入力した場合に、円記号がエスケープ文字として機能しないようにしますphysicists.$$

結論として、ファイルは2D配列として作成され、その要素は各行のループを使用して抽出され、readコマンドの機能を使用して(インデックス付き)配列の要素に単語を割り当てます。

わずかな改善:

上記のコードでは、ファイルphysicists.$$はループへの入力として指定されているwhileため、実際にはreadコマンドに渡されます。whileただし、ループ内で入力を要求する別のコマンドがある場合、これにより問題が発生することがわかりました。たとえば、selectコマンドは標準入力を待機し、whileループ内に配置された場合physicists.$$、コマンドラインでユーザー入力を求めるのではなく、から入力を受け取ります。これを修正するには、ファイル記述子からの読み取りを可能にするの-uオプションを使用します。次のコードのように、 (コマンドを使用して)readファイル記述子を作成し、それを読み取りオプションに指定するだけです。execphysicists.$$-u

## Store the "two-dimensional data" in a file ($$ is just the process ID of the shell, to make sure the filename is unique)
cat > physicists.$$ <<EOF
Wolfgang Pauli 1900
Werner Heisenberg 1901
Albert Einstein 1879
Niels Bohr 1885
EOF
nbPhysicists=$(wc -l physicists.$$ | cut -sf 1 -d ' ')     # Number of lines of the here-file specifying the physicists.
exec {id_file}<./physicists.$$     # Create a file descriptor stored in 'id_file'.

## Extract the needed data
declare -a person     # Create an indexed array (necessary for the read command).                                                                                 
while read -ra person -u "${id_file}"; do
firstName=${person[0]}
familyName=${person[1]}
birthYear=${person[2]}
echo "Physicist ${firstName} ${familyName} was born in ${birthYear}"
# Do whatever you need with data
done

## Close the file descriptor
exec {id_file}<&-
## Remove the temporary file
rm physicists.$$

最後にファイル記述子が閉じていることに注意してください。

于 2017-09-27T12:19:36.467 に答える
2

Paulの答えを拡張する-bashで連想サブ配列を操作する私のバージョンは次のとおりです。

declare -A SUB_1=(["name1key"]="name1val" ["name2key"]="name2val")
declare -A SUB_2=(["name3key"]="name3val" ["name4key"]="name4val")
STRING_1="string1val"
STRING_2="string2val"
MAIN_ARRAY=(
  "${SUB_1[*]}"
  "${SUB_2[*]}"
  "${STRING_1}"
  "${STRING_2}"
)
echo "COUNT: " ${#MAIN_ARRAY[@]}
for key in ${!MAIN_ARRAY[@]}; do
    IFS=' ' read -a val <<< ${MAIN_ARRAY[$key]}
    echo "VALUE: " ${val[@]}
    if [[ ${#val[@]} -gt 1 ]]; then
        for subkey in ${!val[@]}; do
            subval=${val[$subkey]}
            echo "SUBVALUE: " ${subval}
        done
    fi
done

これは、メイン配列(strings / arrays / assoc)の混合値で機能します。配列

ここで重要なのは、サブ配列を一重引用符で囲み、メイン配列内にサブ配列を格納する*代わりに使用して、スペースで区切られた単一の文字列として格納されるようにすることです。@"${SUB_1[*]}"

次に、値をループするときに、その配列から配列を解析するのが簡単になります。IFS=' ' read -a val <<< ${MAIN_ARRAY[$key]}

上記のコードは次のように出力します。

COUNT:  4
VALUE:  name1val name2val
SUBVALUE:  name1val
SUBVALUE:  name2val
VALUE:  name4val name3val
SUBVALUE:  name4val
SUBVALUE:  name3val
VALUE:  string1val
VALUE:  string2val
于 2015-02-12T10:45:44.410 に答える
2

bashで多次元配列を作成するための多くの回答がここにあります。

そして例外なく、すべてが鈍く、使いにくいです。

MDアレイが必須の基準である場合は、次の決定を行う必要があります。

MDアレイをサポートする言語を使用する

私の好みはPerlです。ほとんどの人はおそらくPythonを選ぶでしょう。どちらも機能します。

データを他の場所に保存する

JSONとjqはすでに提案されています。XMLも提案されていますが、JSONとjqを使用する方が簡単な場合があります。

バッシュはあなたがする必要があることのための最良の選択ではないかもしれないように思われるでしょう。

正しい質問は、「ツールYでXを実行するにはどうすればよいですか?」ではなく、「Xを実行するのに最適なツールはどれですか?」である場合があります。

于 2019-09-03T20:45:14.533 に答える
1

これは、 bash 4以降の連想配列を使用し、手動で定義できる値に設定して行います。IFS

このアプローチの目的は、連想配列キーの値として配列を使用することです。

IFSをデフォルトに戻すには、設定を解除するだけです。

  • unset IFS

これは例です:

#!/bin/bash

set -euo pipefail

# used as value in asscciative array
test=(
  "x3:x4:x5"
)
# associative array
declare -A wow=(
  ["1"]=$test
  ["2"]=$test
)
echo "default IFS"
for w in ${wow[@]}; do
  echo "  $w"
done

IFS=:
echo "IFS=:"
for w in ${wow[@]}; do
  for t in $w; do
    echo "  $t"
  done
done
echo -e "\n or\n"
for w in ${!wow[@]}
do
  echo "  $w"
  for t in ${wow[$w]}
  do
    echo "    $t"
  done
done

unset IFS
unset w
unset t
unset wow
unset test

以下のスクリプトの出力は次のとおりです。

default IFS
  x3:x4:x5
  x3:x4:x5
IFS=:
  x3
  x4
  x5
  x3
  x4
  x5

 or

  1
    x3
    x4
    x5
  2
    x3
    x4
    x5
于 2017-06-30T18:00:56.687 に答える
1

Bashは多次元配列をサポートしていませんが、連想配列を使用して実装できます。ここで、インデックスは値を取得するためのキーです。連想配列はbashバージョン4で使用できます。

#!/bin/bash

declare -A arr2d
rows=3
columns=2

for ((i=0;i<rows;i++)) do
    for ((j=0;j<columns;j++)) do
        arr2d[$i,$j]=$i
    done
done


for ((i=0;i<rows;i++)) do
    for ((j=0;j<columns;j++)) do
        echo ${arr2d[$i,$j]}
    done
done
于 2018-04-23T07:54:32.753 に答える
0

非常にシンプルでありながら賢い回避策があります。名前に変数を使用して配列を定義するだけです。例えば:

for (( i=0 ; i<$(($maxvalue + 1)) ; i++ ))
  do
  for (( j=0 ; j<$(($maxargument + 1)) ; j++ ))
    do
    declare -a array$i[$j]=((Your rule))
  done
done

これはあなたが求めていたものとは正確に一致しないため、これが役立つかどうかはわかりませんが、私にとってはうまくいきます。(同じことは、配列のない変数だけでも達成できます)

于 2018-01-18T17:14:42.357 に答える
-3
echo "Enter no of terms"
read count
for i in $(seq 1 $count)
do
  t=` expr $i - 1 `
  for j in $(seq $t -1 0)
  do
    echo -n " "
  done
  j=` expr $count + 1 `
  x=` expr $j - $i `
  for k in $(seq 1 $x)
  do
    echo -n "* "
  done
  echo ""
done
于 2014-01-19T16:44:50.810 に答える