首页 文章

如何在Bash中加入数组元素?

提问于
浏览
323

如果我在Bash中有这样的数组:

FOO=( a b c )

如何用逗号连接元素?例如,生成 a,b,c .

26 回答

  • -1

    重用@并不重要的解决方案,但通过避免$ {:1}替换和需要中间变量的一个声明 .

    echo $(printf "%s," "${LIST[@]}" | cut -d "," -f 1-${#LIST[@]} )
    

    printf has'格式字符串经常被重用,以满足参数 . 在其手册页中,以便记录字符串的连接 . 然后诀窍是使用LIST长度来切断最后一个sperator,因为cut将仅保留LIST的长度作为字段计数 .

  • 6

    顶级答案的缩短版本:

    joinStrings() { local a=("${@:3}"); printf "%s" "$2${a[@]/#/$1}"; }
    

    用法:

    joinStrings "$myDelimiter" "${myArray[@]}"
    
  • 4

    我的尝试 .

    $ array=(one two "three four" five)
    $ echo "${array[0]}$(printf " SEP %s" "${array[@]:1}")"
    one SEP two SEP three four SEP five
    
  • 0
    liststr=""
    for item in list
    do
        liststr=$item,$liststr
    done
    LEN=`expr length $liststr`
    LEN=`expr $LEN - 1`
    liststr=${liststr:0:$LEN}
    

    这也会在最后处理额外的逗号 . 我不是bash专家 . 只是我的2c,因为这是更基本和可理解的

  • -2

    这是一个完成工作的100%纯Bash功能:

    join() {
        # $1 is return variable name
        # $2 is sep
        # $3... are the elements to join
        local retname=$1 sep=$2 ret=$3
        shift 3 || shift $(($#))
        printf -v "$retname" "%s" "$ret${@/#/$sep}"
    }
    

    看:

    $ a=( one two "three three" four five )
    $ join joineda " and " "${a[@]}"
    $ echo "$joineda"
    one and two and three three and four and five
    $ join joinedb randomsep "only one element"
    $ echo "$joinedb"
    only one element
    $ join joinedc randomsep
    $ echo "$joinedc"
    
    $ a=( $' stuff with\nnewlines\n' $'and trailing newlines\n\n' )
    $ join joineda $'a sep with\nnewlines\n' "${a[@]}"
    $ echo "$joineda"
     stuff with
    newlines
    a sep with
    newlines
    and trailing newlines
    
    
    $
    

    这甚至保留了尾随换行符,并且不像 printf -v (你为什么不喜欢它?)并传递变量名,你当然可以为返回的字符串使用全局变量:

    join() {
        # $1 is sep
        # $2... are the elements to join
        # return is in global variable join_ret
        local sep=$1 IFS=
        join_ret=$2
        shift 2 || shift $(($#))
        join_ret+="${*/#/$sep}"
    }
    
  • 8

    Pascal Pilz将重写解决方案作为100%纯Bash中的函数(无外部命令):

    function join_by { local IFS="$1"; shift; echo "$*"; }
    

    例如,

    join_by , a "b c" d #a,b c,d
    join_by / var local tmp #var/local/tmp
    join_by , "${FOO[@]}" #a,b,c
    

    或者,我们可以使用printf来支持多字符分隔符,使用@gniourf_gniourf的想法

    function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
    

    例如,

    join_by , a b c #a,b,c
    join_by ' , ' a b c #a , b , c
    join_by ')|(' a b c #a)|(b)|(c
    join_by ' %s ' a b c #a %s b %s c
    join_by $'\n' a b c #a<newline>b<newline>c
    join_by - a b c #a-b-c
    join_by '\' a b c #a\b\c
    
  • 64

    使用变量间接直接引用数组也有效 . 也可以使用命名引用,但它们仅在4.3中可用 .

    使用这种形式的函数的优点是你可以让分隔符是可选的(默认为默认的第一个字符 IFS ,这是一个空格;如果你愿意,可以使它成为一个空字符串),它避免了两次扩展值(第一个作为参数传递时,第二个作为函数内的 "$@" ) .

    此解决方案也不要求用户在命令替换中调用该函数 - 它会调用子shell,以获取分配给另一个变量的字符串的连接版本 .

    至于缺点:你必须小心传递正确的参数名称,传递 __r 会给你 __r[@] . 变量间接的行为也扩展了其他形式的参数也没有明确记录 .

    function join_by_ref {
        __=
        local __r=$1[@] __s=${2-' '}
        printf -v __ "%s${__s//\%/%%}" "${!__r}"
        __=${__%${__s}}
    }
    
    array=(1 2 3 4)
    
    join_by_ref array
    echo "$__" # Prints '1 2 3 4'.
    
    join_by_ref array '%s'
    echo "$__" # Prints '1%s2%s3%s4'.
    
    join_by_ref 'invalid*' '%s' # Bash 4.4 shows "invalid*[@]: bad substitution".
    echo "$__" # Prints nothing but newline.
    

    这适用于3.1到5.0-alpha . 如上所述,变量间接不仅适用于变量,也适用于其他参数 .

    参数是存储值的实体 . 它可以是特殊参数下面列出的名称,数字或特殊字符之一 . 变量是由名称表示的参数 .

    数组和数组元素也是参数(存储值的实体),对数组的引用在技术上也是对参数的引用 . 和特殊参数 @ 非常相似, array[@] 也是有效的参考 .

    改变或选择性扩展形式(如子串扩展)偏离参数本身的参考不再起作用 .

  • 3
    s=$(IFS=, eval 'echo "${FOO[*]}"')
    
  • 0

    如果要连接的元素不是数组,只是空格分隔的字符串,您可以执行以下操作:

    foo="aa bb cc dd"
    bar=`for i in $foo; do printf ",'%s'" $i; done`
    bar=${bar:1}
    echo $bar
        'aa','bb','cc','dd'
    

    例如,我的用例是我的shell脚本中传递了一些字符串,我需要使用它来运行SQL查询:

    ./my_script "aa bb cc dd"
    

    在my_script中,我需要做“SELECT * FROM table WHERE name IN('aa','bb','cc','dd') . 然后上面的命令将很有用 .

  • 18

    也许我是整个bash / zsh的新手,但它看起来像你根本不需要使用 printf . 如果没有它也不会变得非常难看 .

    join() {
      separator=$1
      arr=$*
      arr=${arr:2} # throw away separator and following space
      arr=${arr// /$separator}
    }
    

    至少,它迄今为止对我有用而没有问题 .

    例如, join \| *.sh ,在我的 ~ 目录中让's say I' m输出 utilities.sh|play.sh|foobar.sh . 对我来说足够好了 .

    编辑:这基本上是Nil Geisweiller's answer,但推广到一个函数 .

  • 4
    $ set a 'b c' d
    
    $ history -p "$@" | paste -sd,
    a,b c,d
    
  • 422

    如果您在循环中构建数组,这是一个简单的方法:

    arr=()
    for x in $(some_cmd); do
       arr+=($x,)
    done
    arr[-1]=${arr[-1]%,}
    echo ${arr[*]}
    
  • 1
    $ foo=(a "b c" d)
    $ bar=$(IFS=, ; echo "${foo[*]}")
    $ echo "$bar"
    a,b c,d
    
  • 183

    我会将数组作为字符串回显,然后将空格转换为换行符,然后使用 paste 将所有内容连接到一行中,如下所示:

    tr " " "\n" <<< "$FOO" | paste -sd , -

    结果:

    a,b,c

    这对我来说似乎是最快最干净的!

  • -2

    使用perl作为多字符分隔符:

    function join {
       perl -e '$s = shift @ARGV; print join($s, @ARGV);' "$@"; 
    }
    
    join ', ' a b c # a, b, c
    

    或者在一行中:

    perl -le 'print join(shift, @ARGV);' ', ' 1 2 3
    1, 2, 3
    
  • 1

    也许,例如,

    SAVE_IFS="$IFS"
    IFS=","
    FOOJOIN="${FOO[*]}"
    IFS="$SAVE_IFS"
    
    echo "$FOOJOIN"
    
  • 0

    令人惊讶的是我的解决方案尚未给出:)这对我来说是最简单的方法 . 它不需要功能:

    IFS=, eval 'joined="${foo[*]}"'
    

    注意:观察到此解决方案在非POSIX模式下运行良好 . 在POSIX mode中,元素仍然正确连接,但 IFS=, 变为永久性 .

  • 22

    到目前为止,将最好的世界与以下想法相结合 .

    # join with separator
    join_ws()  { local IFS=; local s="${*/#/$1}"; echo "${s#"$1$1$1"}"; }
    

    这个小小的杰作是

    • 100%纯bash(暂时取消设置IFS的参数扩展,没有外部调用,没有printf ...)

    • 紧凑,完整和完美(适用于单字符和多字符限制器,适用于包含空格的限制器,换行符和其他shell特殊字符,使用空分隔符)

    • 高效(没有子shell,没有数组副本)

    • 简单而愚蠢,在某种程度上,美丽和也很有启发性

    例子:

    $ join_ws , a b c
    a,b,c
    $ join_ws '' a b c
    abc
    $ join_ws $'\n' a b c
    a
    b
    c
    $ join_ws ' \/ ' A B C
    A \/ B \/ C
    
  • 1

    这种方法处理值中的空格,但需要循环:

    #!/bin/bash
    
    FOO=( a b c )
    BAR=""
    
    for index in ${!FOO[*]}
    do
        BAR="$BAR,${FOO[$index]}"
    done
    echo ${BAR:1}
    
  • 10

    现在我正在使用:

    TO_IGNORE=(
        E201 # Whitespace after '('
        E301 # Expected N blank lines, found M
        E303 # Too many blank lines (pep8 gets confused by comments)
    )
    ARGS="--ignore `echo ${TO_IGNORE[@]} | tr ' ' ','`"
    

    哪个有效,但(在一般情况下)如果数组元素中有空格,则可能会破坏 .

    (对于那些感兴趣的人,这是一个围绕pep8.py的包装脚本)

  • 106

    这不是在父shell中修改 IFS 并且都在一行中:

    arr=(a b c)
    printf '%s\n' "$(IFS=,; echo "${arr[*]}")"
    

    导致

    a,b,c
    
  • 7

    又一个解决方案:

    #!/bin/bash
    foo=('foo bar' 'foo baz' 'bar baz')
    bar=$(printf ",%s" "${foo[@]}")
    bar=${bar:1}
    
    echo $bar
    

    编辑:相同但对于多字符可变长度分隔符:

    #!/bin/bash
    separator=")|(" # e.g. constructing regex, pray it does not contain %s
    foo=('foo bar' 'foo baz' 'bar baz')
    regex="$( printf "${separator}%s" "${foo[@]}" )"
    regex="${regex:${#separator}}" # remove leading separator
    echo "${regex}"
    # Prints: foo bar)|(foo baz)|(bar baz
    
  • 1

    不使用外部命令:

    $ FOO=( a b c )     # initialize the array
    $ BAR=${FOO[@]}     # create a space delimited string from array
    $ BAZ=${BAR// /,}   # use parameter expansion to substitute spaces with comma
    $ echo $BAZ
    a,b,c
    

    警告,它假定元素没有空格 .

  • 3

    感谢@gniourf_gniourf对我迄今为止最好的世界组合的详细评论 . 很抱歉发布未完全设计和测试的代码 . 这是一个更好的尝试 .

    # join with separator
    join_ws() { local d=$1 s=$2; shift 2 && printf %s "$s${@/#/$d}"; }
    

    这种美观的概念是

    • (仍然)100%纯粹的bash(感谢明确指出printf也是内置的 . 我之前没有意识到这一点......)

    • 适用于多字符分隔符

    • 更紧凑,更完整,这一次仔细考虑和长期压力测试与shell脚本等随机子串,包括使用shell特殊字符或控制字符或在分隔符和/或参数和边缘没有字符案件,角落案件和其他几乎没有参数的狡辩 . 这并不能保证不再有bug,但找到一个会有点难度 . 顺便说一句,即使是目前最高投票的答案和相关的东西都受到像这样的事情的影响......

    其他例子:

    $ join_ws '' a b c
    abc
    $ join_ws ':' {1,7}{A..C}
    1A:1B:1C:7A:7B:7C
    $ join_ws -e -e
    -e
    $ join_ws $'\033[F' $'\n\n\n'  1.  2.  3.  $'\n\n\n\n'
    3.
    2.
    1.
    $ join_ws $ 
    $
    
  • 1
    awk -v sep=. 'BEGIN{ORS=OFS="";for(i=1;i<ARGC;i++){print ARGV[i],ARGC-i-1?sep:""}}' "${arr[@]}"
    

    要么

    $ a=(1 "a b" 3)
    $ b=$(IFS=, ; echo "${a[*]}")
    $ echo $b
    1,a b,3
    
  • 1

    printf解决方案,接受任何长度的分隔符(基于@并不重要)

    #/!bin/bash
    foo=('foo bar' 'foo baz' 'bar baz')
    
    sep=',' # can be of any length
    bar=$(printf "${sep}%s" "${foo[@]}")
    bar=${bar:${#sep}}
    
    echo $bar
    

相关问题