将Bash数组转换为分隔的字符串

时间:2012-11-20 09:45:10

标签: arrays string bash delimited-text

我想知道以下内容;

  1. 为什么给定的非工作示例不起作用。
  2. 如果除了工作示例中给出的方法之外还有其他更清洁的方法。
  3. 非工作示例

    > ids=(1 2 3 4);echo ${ids[*]// /|}
    1 2 3 4
    > ids=(1 2 3 4);echo ${${ids[*]}// /|}
    -bash: ${${ids[*]}// /|}: bad substitution
    > ids=(1 2 3 4);echo ${"${ids[*]}"// /|}
    -bash: ${"${ids[*]}"// /|}: bad substitution
    

    工作示例

    > ids=(1 2 3 4);id="${ids[@]}";echo ${id// /|}
    1|2|3|4
    > ids=(1 2 3 4); lst=$( IFS='|'; echo "${ids[*]}" ); echo $lst
    1|2|3|4
    

    在上下文中,用于sed命令的分隔字符串,用于进一步解析。

4 个答案:

答案 0 :(得分:29)

# REVISION: 2017-03-14
# Use of read and other bash specific features (bashisms)

因为括号用于分隔数组,而不是字符串:

ids="1 2 3 4";echo ${ids// /|}
1|2|3|4

部分示例:使用两个字符串填充$idsa bc d

ids=("a b" "c d")

echo ${ids[*]// /|}
a|b c|d

IFS='|';echo "${ids[*]}";IFS=$' \t\n'
a b|c d

......最后:

IFS='|';echo "${ids[*]// /|}";IFS=$' \t\n'
a|b|c|d

组装数组时,由$IFS的第一个字符分隔,但在数组的每个元素中用|替换空格。

当你这样做时:

id="${ids[@]}"

您将字符串构建从数组 ids合并为空格转换为字符串类型的新变量。

注意:"${ids[@]}"提供以空格分隔的字符串时,"${ids[*]}"(使用星号*代替at @ sign $IFS)将呈现由 man bash 的第一个字符分隔的字符串。

man -Len -Pcol\ -b bash | sed -ne '/^ *IFS /{N;N;p;q}' IFS The Internal Field Separator that is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is ``<space><tab><newline>''. 说的是什么:

$IFS

使用set | grep ^IFS= IFS=$' \t\n'

declare -p IFS
declare -- IFS=" 
"
printf "%q\n" "$IFS"
$' \t\n'

space

字面意思是tabulationline-feed(含义或) a *。所以,虽然第一个角色是一个空间。使用@的方式与{ 相同。

<强>但是

    # OIFS="$IFS"
    # IFS=$': \t\n'
    # unset array 
    # declare -a array=($(echo root:x:0:0:root:/root:/bin/bash))

    IFS=: read -a array < <(echo root:x:0:0:root:/root:/bin/bash)

    echo 1 "${array[@]}"
    echo 2 "${array[*]}"
    OIFS="$IFS" IFS=:
    echo 3 "${array[@]}"
    echo 4 "${array[*]}"
    IFS="$OIFS"
}
1 root x 0 0 root /root /bin/bash
2 root x 0 0 root /root /bin/bash
3 root x 0 0 root /root /bin/bash
4 root:x:0:0:root:/root:/bin/bash

IFS=: read -a array < <(...)

注意::将使用$IFS作为分隔符,而不会永久设置#2。这是因为输出行{{1}}将空格显示为分隔符。

答案 1 :(得分:12)

F. Hauri's answer已经解决了您的第一个问题。这是连接数组元素的规范方法:

ids=( 1 2 3 4 )
IFS=\| eval 'lst="${ids[*]}"'

有些人会大声喊叫eval是邪恶的,但由于单引号,这里非常安全。这只有一个优点:没有子shell,IFS没有全局修改,它不会修剪尾随换行符,而且非常简单。

答案 2 :(得分:4)

您也可以使用printf,无需任何外部命令或需要操作IFS:

ids=(1 2 3 4)                     # create array
printf -v ids_d '|%s' "${ids[@]}" # yields "|1|2|3|4"
ids_d=${ids_d:1}                  # remove the leading '|'

答案 3 :(得分:1)

一个实用程序函数,用于将参数数组连接到分隔的字符串中:

sortrows

输出:

#!/usr/bin/env bash

# Join arguments with delimiter
# @Params
# $1: The delimiter string
# ${@:2}: The arguments to join
# @Output
# >&1: The arguments separated by the delimiter string
array::join() {
  (($#)) || return 1 # At least delimiter required
  local -- delim="$1" str IFS=
  shift
  str="${*/#/$delim}" # Expand arguments with prefixed delimiter (Empty IFS)
  echo "${str:${#delim}}" # Echo without first delimiter
}

declare -a my_array=( 'Paris' 'Berlin' 'London' 'Brussel' 'Madrid' 'Oslo' )

array::join ', ' "${my_array[@]}"
array::join '*' {1..9} | bc # 1*2*3*4*5*6*7*8*9=362880 Factorial 9

declare -a null_array=()

array::join '== Ultimate separator of nothing ==' "${null_array[@]}"