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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1527049/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:35:15  来源:igfitidea点击:

How can I join elements of an array in Bash?

arraysbash

提问by David Wolever

If I have an array like this in Bash:

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

FOO=( a b c )

How do I join the elements with commas? For example, producing a,b,c.

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

回答by Nicholas Sushkin

Rewriting solution by Pascal Pilz as a function in 100% pure Bash (no external commands):

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

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

For example,

例如,

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

Alternatively, we can use printf to support multi-character delimiters, using the idea by @gniourf_gniourf

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

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

For example,

例如,

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

回答by doesn't matters

Yet another solution:

另一个解决方案:

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

echo $bar

Edit: same but for multi-character variable length separator:

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

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

回答by Pascal Pilz

$ foo=(a "b c" d)
$ bar=$(IFS=, ; echo "${foo[*]}")
$ echo "$bar"
a,b c,d

回答by martin clayton

Maybe, e.g.,

也许,例如,

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

echo "$FOOJOIN"

回答by konsolebox

Surprisingly my solution is not yet given :) This is the simplest way for me. It doesn't need a function:

令人惊讶的是,我的解决方案还没有给出:) 这对我来说是最简单的方法。它不需要函数:

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

Note: This solution was observed to work well in non-POSIX mode. In POSIX mode, the elements are still joined properly, but IFS=,becomes permanent.

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

回答by gniourf_gniourf

Here's a 100% pure Bash function that does the job:

这是一个 100% 纯 Bash 函数来完成这项工作:

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

Look:

看:

$ 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


$

This preserves even the trailing newlines, and doesn't need a subshell to get the result of the function. If you don't like the printf -v(why wouldn't you like it?) and passing a variable name, you can of course use a global variable for the returned string:

这甚至保留了尾随的换行符,并且不需要子shell来获取函数的结果。如果您不喜欢printf -v(为什么不喜欢它?)并传递变量名,您当然可以对返回的字符串使用全局变量:

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

回答by Benjamin W.

This isn't all too different from existing solutions, but it avoids using a separate function, doesn't modify IFSin the parent shell and is all in a single line:

这与现有的解决方案并没有太大的不同,但它避免使用单独的函数,不在IFS父 shell 中修改并且全部在一行中:

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

resulting in

导致

a,b,c

Limitation: the separator can't be longer than one character.

限制:分隔符不能超过一个字符。

回答by Nil Geisweiller

Using no external commands:

不使用外部命令:

$ 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

Warning, it assumes elements don't have whitespaces.

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

回答by Yanick Girouard

I would echo the array as a string, then transform the spaces into line feeds, and then use pasteto join everything in one line like so:

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

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

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

Results:

结果:

a,b,c

a,b,c

This seems to be the quickest and cleanest to me !

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

回答by Valise

With re-use of @doesn't matters' solution, but with a one statement by avoiding the ${:1} substition and need of an intermediary variable.

重新使用@doesn't matter' 的解决方案,但是通过避免 ${:1} 替换和需要中间变量来使用 one 语句。

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

printf has 'The format string is reused as often as necessary to satisfy the arguments.' in its man pages, so that the concatenations of the strings is documented. Then the trick is to use the LIST length to chop the last sperator, since cut will retain only the lenght of LIST as fields count.

printf 有“格式字符串会根据需要重复使用以满足参数。” 在其手册页中,以便记录字符串的连接。然后诀窍是使用 LIST 长度来切割最后一个 sperator,因为 cut 将只保留 LIST 的长度作为字段计数。