【问题标题】:Converting a Bash array into a delimited string将 Bash 数组转换为分隔字符串
【发布时间】:2012-11-08 08:31:54
【问题描述】:

我想知道以下内容;

  1. 为什么给定的无效示例不起作用。
  2. 如果有任何其他比工作示例中给出的更清洁的方法。

非工作示例

> 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 命令中用于进一步解析的分隔字符串。

【问题讨论】:

标签: arrays string bash csv


【解决方案1】:

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

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[@]}"

您将字符串构建从 array ids 通过空格的合并转移到 string 类型的新变量。

注意:"${ids[@]}" 给出一个空格分隔的 字符串时,"${ids[*]}"(用星号* 代替@)将呈现由$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:

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

字面意思是 spacetabulation(意思是或) line-feed。所以,虽然第一个字符是一个空格。 * 的使用与@ 的作用相同。

但是

{
    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 &lt; &lt;(...) 行将使用: 作为分隔符,不会永久设置$IFS。这是因为输出行#2 将空格作为分隔符。

【讨论】:

  • 所以它既是 typeof 又是变量替换错误,因为 ${ 期望一个字符串类型的 var 但两者都没有收到。谢谢你的详细解释。
  • 也可以跳过字符串赋值,仍然将数组转换为带有任意分隔符的分隔字符串,不一定是单个字符,使用printf '%smyDelim' "${array[@]}"。分隔符myDelim 的最后一个实例可以通过管道删除到sed -e 's/myDelim$//',但这很麻烦。更好的想法?
  • @JonathanY。如果是这样,请使用 printf -v myVar '%smyDelim' "${array[@]}"; myVar="${myVar%myDelim}" 而不是 fork 到 sed
  • 除非不跳过变量赋值,例如,当使用printf 为另一个可执行文件提供输入参数时。我知道分配一个变量可能更快更便宜。它只是感觉不那么优雅。
  • @F.Hauri 您必须在 myDelim 周围加上双引号。您还必须有 $ 符号。精简这个“%s$myDelim”
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    您也可以使用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 '|'
    

    【讨论】:

    • 这对我来说是最简单的,因为我在数组元素之间放置了 >1 个字符串,在我的情况下,我也不需要删除“额外”。工作愉快! printf -v strng "'%s',\n" ${thearray[*]} :)
    • 同意,使用最简单。此外,如果您使用带有语法突出显示的 IDE,它也是用户友好的选项,它不会像 gniourf_gniourf's answer 那样选择 eval'd 语法。
    【解决方案4】:

    将参数数组连接成分隔字符串的实用函数:

    #!/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[@]}"
    

    输出:

    Paris, Berlin, London, Brussel, Madrid, Oslo
    362880
    
    

    【讨论】:

    • 非常方便,谢谢!不过,也许join 是一个更合适的名称,但与例如PerlPython'sjoin 函数的工作原理一致?
    • @TheDudeAbides 无法直接命名 join,因为这与连接文件行的现有命令名称冲突。
    • *facepalm* - 是的,当然。我忘了that
    • 不错的工具!允许可变长度分隔符!但考虑使用 nameref 将结果作为变量传递,以防止分叉...
    猜你喜欢
    • 2019-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    • 2014-07-01
    • 1970-01-01
    相关资源
    最近更新 更多