ピリオドで区切られたフィールドを含む行ごとに入力ファイルを読み取ろうとしています。それらを後でループできるように、配列の配列に入れたいです。入力は問題ないように見えますが、配列(inData)に「プッシュ」しても機能していないようです。
コードは次のとおりです。
Input file:
GSDB.GOSALESDW_DIST_INVENTORY_FACT.MONTH_KEY
GSDB.GOSALESDW_DIST_INVENTORY_FACT.ORGANIZATION_KEY
infile=${1}
OIFS=$IFS
IFS=":"
cat ${infile} | while read line
do
line=${line//\./:}
inarray=(${line})
# echo ${inarray[@]}
# echo ${#inarray[@]}
# echo ${inarray[0]}
# echo ${inarray[1]}
# echo ${inarray[2]}
inData=("${inData[@]}" "${inarray[@]}")
done
IFS=$OIFS
echo ${#inData[@]}
for ((i = 0; i < ${#inData[@]}; i++))
do
echo $i
for ((j = 0; j < ${#inData[$i][@]}; j++))
do
echo ${inData[$i][$j]}
done
done
Bashは多次元配列をサポートしていません。試して
array=(a b c d)
echo ${array[1]}
echo ${array[1][3]}
echo ${array[1]exit}
それらをシミュレートする方法については、 Advanced Bash Scripting Guide を参照してください。
Bashのフィールドネストボックスですが、例を参照してください。
#!/bin/bash
# requires bash 4 or later; on macOS, /bin/bash is version 3.x,
# so need to install bash 4 or 5 using e.g. https://brew.sh
declare -a pages
pages[0]='domain.de;de;https'
pages[1]='domain.fr;fr;http'
for page in "${pages[@]}"
do
# turn e.g. 'domain.de;de;https' into
# array ['domain.de', 'de', 'https']
IFS=";" read -r -a arr <<< "${page}"
site="${arr[0]}"
lang="${arr[1]}"
prot="${arr[2]}"
echo "site : ${site}"
echo "lang : ${lang}"
echo "prot : ${prot}"
echo
done
文字列を「配列」に分割できることを知っている。リストのリストを作成できます。たとえば、DBサーバー内のデータベースのリストのように。
dbServersList=('db001:app001,app002,app003' 'db002:app004,app005' 'dbcentral:central')
# Loop over DB servers
for someDbServer in ${dbServersList[@]}
do
# delete previous array/list (this is crucial!)
unset dbNamesList
# split sub-list if available
if [[ $someDbServer == *":"* ]]
then
# split server name from sub-list
tmpServerArray=(${someDbServer//:/ })
someDbServer=${tmpServerArray[0]}
dbNamesList=${tmpServerArray[1]}
# make array from simple string
dbNamesList=(${dbNamesList//,/ })
fi
# Info
echo -e "\n----\n$someDbServer\n--"
# Loop over databases
for someDB in ${dbNamesList[@]}
do
echo $someDB
done
done
上記の出力は次のようになります。
----
db001
--
app001
app002
app003
----
db002
--
app004
app005
----
dbcentral
--
central
私はこれに苦労しましたが、不快な妥協案を見つけました。一般に、Bashでデータ構造を使用することを含むソリューションの問題に直面した場合、Pythonなどの別の言語に切り替える必要があります。そのアドバイスを無視して、すぐに進む:
私のユースケースには通常、リストのリスト(または配列の配列)とそれらのループが含まれます。通常、それよりも深くネストすることは望ましくありません。また、ほとんどの配列は文字列であり、スペースを含む場合と含まない場合がありますが、通常は特殊文字を含みません。これにより、混乱しない構文を使用して外部配列を表現し、文字列に対して通常のbash処理を使用して2番目のリストまたは配列を取得できます。 IFS区切り文字obviに注意する必要があります。
したがって、連想配列を使用すると、次のようなリストのリストを作成できます。
declare -A JOB_LIST=(
[job1] = "a set of arguments"
[job2] = "another different list"
...
)
これにより、次のように両方の配列を反復処理できます。
for job in "${!JOB_LIST[@]}"; do
/bin/jobrun ${job[@]}
done
ああ、キーリストの出力(魔法の${!...}
)は、リストを順番にたどらないことを意味します。したがって、もう1つ必要なハックは、キーの順序を並べ替えることです(重要な場合)。ソート順はあなた次第です。英数字のソートを使用し、aajob1 bbjob3 ccjob6
は完全に受け入れられます。
だから
declare -A JOB_LIST=(
[aajob1] = "a set of arguments"
[bbjob2] = "another different list"
...
)
sorted=($(printf '%s\n' "${!JOB_LIST[@]}"| /bin/sort))
for job in "${sorted[@]}"; do
for args in "${job[@]}"; do
echo "Do something with ${arg} in ${job}"
done
done
連想配列を使用し、キーで::を使用して深さを示します。 ::は属性の埋め込みにも使用できますが、それは別の主題です...
declare -A __myArrayOfArray=([Array1::Var1]="Assignment" [Array2::Var1]="Assignment")
Array1の下の配列
__myArrayOfArray[Array1::SubArray1::Var1]="Assignment"
任意の配列のエントリは、...によって(順番に)取得できます...
local __sortedKeys=`echo ${!__myArrayOfArray[@]} | xargs -n1 | sort -u | xargs`
for __key in ${__sortedKeys}; do
#
# show all properties in the Subordinate Profile "Array1::SubArray1::"
if [[ ${__key} =~ ^Array1::SubArray1:: ]]; then
__property=${__key##Array1::SubArray1::}
if [[ ${__property} =~ :: ]]; then
echo "Property ${__property%%:*} is a Subordinate array"
else
echo "Property ${__property} is set to: ${__myArrayOfArray[${__key}]}"
fi
fi
done
下位の「プロファイル」のリストは、次の方法で導出できます。
declare -A __subordinateProfiles=()
local __profile
local __key
for __key in "${!__myArrayOfArray[@]}"; do
if [[ $__key =~ :: ]]; then
local __property=${__key##*:}
__profile=${__key%%:*}
__subordinateProfiles[${__profile}]=1
fi
done
次のスクリプトのように、配列の(逆)参照を使用できます。
#!/bin/bash
OFS=$IFS # store field separator
IFS="${2: }" # define field separator
file=$1 # input file name
unset a # reference to line array
unset i j # index
unset m n # dimension
### input
i=0
while read line
do
a=A$i
unset $a
declare -a $a='($line)'
i=$((i+1))
done < $file
# store number of lines
m=$i
### output
for ((i=0; i < $m; i++))
do
a=A$i
# get line size
# double escape '\\' for sub Shell '``' and 'echo'
n=`eval echo \\${#$a[@]}`
for (( j = 0; j < $n; j++))
do
# get field value
f=`eval echo \\${$a[$j]}`
# do something
echo "line $((i+1)) field $((j+1)) = '$f'"
done
done
IFS=$OFS