如何在数组的每个成员之前添加和追加?

如何在数组的每个成员之前添加和追加?

我有一个数组:

CATEGORIES=(one two three four)

我可以使用参数扩展在每个数组成员前面添加:

echo ${CATEGORIES[@]/#/foo }

我可以以相同的方式附加到每个数组成员:

echo ${CATEGORIES[@]/%/ bar}

我怎样才能两者兼得?这些都不起作用:

echo ${CATEGORIES[@]/(.*)/foo \1 bar}
echo ${CATEGORIES[@]/(.*)/foo $1 bar}
echo ${CATEGORIES[@]/(.*)/foo ${BASH_REMATCH[1]} bar}

答案1

根据您的最终目标,您可以使用printf

$ a=(1 2 3)
$ printf "foo %s bar\n" "${a[@]}"
foo 1 bar
foo 2 bar
foo 3 bar

printf重新使用格式字符串,直到所有参数都用完,因此它提供了一种将某些格式应用于一组字符串的简单方法。

答案2

根据记录, with zsh,有一个${^array}运算符可以在数组元素上打开类似大括号的扩展。所以:

$ a=(one two three)
$ b=('foo '${^a}' bar')
$ printf '<%s>\n' $b
<foo one bar>
<foo two bar>
<foo three bar>

搜索和替换也适用于zsh.

$ printf '<%s>\n' ${a//(#m)*/foo $MATCH bar}
<foo one bar>
<foo two bar>
<foo three bar>

以及printf -v在数组上:

$ b=(); printf -v b 'foo %s bar' "$a[@]"
$ printf '<%s>\n' $b
<foo one bar>
<foo two bar>
<foo three bar>

如果echo ${CATEGORIES[@]/(.*)/foo \1 bar}写成ksh93

$ printf '<%s>\n' "${CATEGORIES[@]/@(.*)/foo \1 bar}"
<foo one bar>
<foo two bar>
<foo three bar>

答案3

p='* "foo  '
s='  bar $USER' 
CATEGORIES=(one two three four)
CATEGORIES=("${CATEGORIES[@]/#/$p}")
CATEGORIES=("${CATEGORIES[@]/%/$s}")

paste <(printf '[%s]\n' "${!CATEGORIES[@]}") \
      <(printf '%s\n'    "${CATEGORIES[@]}")

输出:

[0] * "foo  one  bar $USER
[1] * "foo  two  bar $USER
[2] * "foo  three  bar $USER
[3] * "foo  four  bar $USER

答案4

这是一个特例当附加(或前置)字符串是一个时适合的解决方案单个字符,并且不需要新数组中的值:

array=( aa bb cc )
IFS="]"                        # or, IFS="["
echo "${array[*]/#/ [}$IFS"    # or, echo "$IFS${array[*]/%/] }

产生输出[aa] [bb] [cc].

  • 带引号的形式在每对之间"${array[*]}"添加分隔符( 的第一个字符,这是出现约束的地方)]IFS
  • ${array[*]/#/ [}添加 [到每个元素的前面(或/%/附加的形式)
  • 最后添加一个尾随](from IFS)到扩展值

如果一一应用这些步骤,您将得到:

aa]bb]cc
 [aa] [bb] [cc
 [aa] [bb] [cc]

(您也可以轻松地将数据恢复为新数组如果这些值不包含空格。)

您可以在一行中执行不同的前缀/后缀操作:

for ii in "${array[@]/#/foo }"; do echo "${ii/%/ bar}"; done

这是一个更强大的printf解决方案,可以复制到新数组:

mapfile -d '' newarray < <(printf "foo %s bar\0" "${array[@]}")

尽管以牺牲子 shell 为代价(需要 bash-4.4 mapfile -d

最后是一个循环变体,它复制到新数组,并且如果需要的话还可以处理稀疏数组和关联数组。

declare -a array newarray   # -a for indexed array, -A for associative
array=( one two three )
for ii in "${!array[@]}"; do
  printf -v "newarray[$ii]" "foo %s bar" "${array[$ii]}"
done

printf不是必需的,您可以直接分配,但恕我直言,它更清楚。bash(还!)支持打印到数组中,但是zsh支持打印到数组中,为您提供无循环复制和转换单行,请参阅上面 Stéphane 的答案。)


这里有用的是,如果bash支持通用&(如$MATCHzsh作为扩展中匹配字符串的占位符。代码就在那里(并且已经很长一段时间了),但遗憾的是它尚未启用(请shouldexp_replacement()参阅subst.c)。如果启用它(两次#if 0更改,然后重新编译),这将按预期工作:

array=( aa bb cc )
newarray=( "${array[@]/*/foo & bar}" )

没关系,也许在下一个版本中可用......

compgen有前缀/后缀操作(并且支持&,但不是我们可以在此处使用的方式)。我们能做的最好的事情还不如穆鲁的printf解决方案:

compgen -P "foo " -S " bar" -W "${array[*]}"

(注意-W只需要一个选项,因此数组被展平,这会导致带有空格或任何内容的值出现问题IFS

相关内容