51

我正在尝试逐行读取包含由句点分隔的字段的输入文件。我想将它们放入一个数组数组中,以便稍后循环遍历它们。输入似乎没问题,但是将其“推送”到数组(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
4

7 回答 7

31

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
于 2015-12-24T13:37:25.023 回答
29

Bash 不支持多维数组。尝试

array=(a b c d)
echo ${array[1]}
echo ${array[1][3]}
echo ${array[1]exit}

有关如何模拟它们的技巧,请参阅Advanced Bash Scripting Guide

于 2012-09-07T12:20:00.207 回答
14

知道您可以将字符串拆分为“数组”。您可以创建一个列表列表。例如,数据库服务器中的数据库列表。

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
于 2016-03-01T16:25:02.250 回答
3

我为此苦苦挣扎,但找到了一个令人不舒服的妥协。通常,当遇到解决方案涉及在 Bash 中使用数据结构的问题时,您应该切换到另一种语言,例如 Python。忽略该建议并继续前进:

我的用例通常涉及列表列表(或数组数组)并循环它们。你通常不想嵌套比这更深。此外,大多数数组是可能包含也可能不包含空格的字符串,但通常不包含特殊字符。这允许我使用不混淆的语法来表达外部数组,然后对字符串使用正常的 bash 处理来获得第二个列表或数组。您需要注意您的 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

啊,除了键列表的输出(使用魔法${!...})意味着你不会按顺序遍历你的列表。因此,另一个必要的技巧是对键的顺序进行排序,如果这对您很重要的话。排序顺序由您决定;我发现使用字母数字排序很方便,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
于 2017-08-10T16:08:32.977 回答
1

我使用关联数组并在键中使用 :: 来表示深度。:: 也可以用于嵌入属性,但这是另一个主题,...

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
于 2018-08-21T19:46:16.233 回答
0

您可以像在此脚本中那样使用(取消)引用数组:

#!/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

归功于https://unix.stackexchange.com/questions/199348/dynamically-create-array-in-bash-with-variables-as-array-name

于 2016-03-04T22:18:26.817 回答
0

如果您使用 declare -p 将每个数组转换并存储为字符串(请参阅我的函数 stringify),则可以使用 bash 数组数组。这将正确处理数组中的空格和任何其他问题字符。提取数组时,使用函数 unstringify 重建数组。此脚本演示了一个数组数组:

#!/bin/bash 
# BASH array of arrays demo

# Convert an array to a string that can be used to reform 
# the array as a new variable. This allows functions to
# return arrays as strings. Works for arrays and associative
# arrays. Spaces and odd characters are all handled by bash 
# declare.
# Usage: stringify variableName
#     variableName - Name of the array variable e.g. "myArray",
#          NOT the array contents.
# Returns (prints) the stringified version of the array.
# Examples. Use declare to make an array:
#     declare -a myArray=( "O'Neal, Dan" "Kim, Mary Ann" )
# (Or to make a local variable replace declare with local.)
# Stringify myArray:
#     stringifiedArray="$(stringify myArray)"
# Reform the array with any name like reformedArray:
#     eval "$(unstringify reformedArray "$stringifiedArray")"
# To stringify an argument list "$@", first create the array
# with a name:     declare -a myArgs=( "$@" )
stringify() {
    declare -p $1
}

# Reform an array from a stringified array. Actually this prints
# the declare command to form the new array. You need to call 
# eval with the result to make the array.
# Usage: eval "$(unstringify newArrayName stringifiedArray [local])"
#     Adding the optional "local" will create a local variable 
#     (uses local instead of declare).
# Example to make array variable named reformedArray from 
# stringifiedArray:
#     eval "$(unstringify reformedArray "$stringifiedArray")"
unstringify() {
    local cmd="declare"
    [ -n "$3" ] && cmd="$3"
    # This RE pattern extracts 2 things:
    #     1: the array type, should be "-a" or "-A"
    #     2: stringified contents of the array 
    # and skips "declare" and the original variable name.
    local declareRE='^declare ([^ ]+) [^=]+=(.*)$'
    if [[ "$2" =~ $declareRE ]]
    then
        printf '%s %s %s=%s\n' "$cmd" "${BASH_REMATCH[1]}" "$1" "${BASH_REMATCH[2]}"
    else
        echo "*** unstringify failed, invalid stringified array:" 1>&2
        printf '%s\n' "$2" 1>&2
        return 1
    fi
}

# array of arrays demo
declare -a array # the array holding the arrays
declare -a row1=( "this is" "row 1" )
declare -a row2=( "row 2" "has problem chars" '!@#$%^*(*()-_=+[{]}"|\:;,.<.>?/' )
declare -a row3=( "$@" ) # row3 is the arguments to the script

# Fill the array with each row converted to a string.
# stringify needs the NAME OF THE VARIABLE, not the variable itself
array[0]="$(stringify row1)"
array[1]="$(stringify row2)"
array[2]="$(stringify row3)"

# Print array contents
for row in "${array[@]}"
do
    echo "Expanding stringified row: $row"
    # Reform the row as the array thisRow
    eval "$(unstringify thisRow "$row")"
    echo "Row values:"
    for val in "${thisRow[@]}"
    do
        echo "   '$val'"
    done
done
于 2021-05-15T17:58:18.363 回答