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



我想知道以下内容;

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

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

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=$' tn'
a b|c d

最后:

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

其中数组是组装的,由$IFS的第一个字符分隔,但数组的每个元素中的空间由|替换。

当你这样做:

id="${ids[@]}"

数组ids合并一个空格后的字符串构建转移到字符串类型的新变量。

注意:"${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 "%qn" "$IFS"
$' tn'

字面上是spacetabulation(意思是或)line-feed。所以,虽然第一个字符是一个空格。CCD_ 17的使用将与CCD_。

但是

{
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将空格表示为分隔符。

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

F.Hauri的回答中已经提到了您的第一个问题。以下是连接数组元素的规范方法:

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

有些人会大声疾呼eval是邪恶的,但多亏了这句话,它在这里是完全安全的。这只具有优点:没有子shell,IFS没有全局修改,不会修剪尾部换行符,而且非常简单。

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

#!/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}" # Expands arguments with prefixed delimiter (Empty IFS)
printf '%sn' "${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

现在有了Bash 4.2+的nameref变量,就不再需要使用子shell输出捕获了。

#!/usr/bin/env bash
if ((BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[0] < 2)))
then
printf 'Bash version 4.2 or above required for nameref variablesn' >&2
exit 1
fi
# Join arguments with delimiter
# @Params
# $1: The variable reference to receive the joined output
# $2: The delimiter string
# ${@:3}: The arguments to join
# @Output
array::join_to() {
(($# > 1)) || return 1 # At least nameref and delimiter required
local -n out="$1"
local -- delim="$2" str IFS=
shift 2
str="${*/#/$delim}" # Expands arguments with prefixed delimiter (Empty IFS)
# shellcheck disable=SC2034 # Nameref variable
out="${str:${#delim}}" # Discards prefixed delimiter
}
declare -g result1 result2 result3
declare -a my_array=( 'Paris' 'Berlin' 'London' 'Brussel' 'Madrid' 'Oslo' )
array::join_to result1 ', ' "${my_array[@]}"
array::join_to result2 '*' {1..9}
result2=$((result2)) # Expands arythmetic expression
declare -a null_array=()
array::join_to result3 '== Ultimate separator of nothing ==' "${null_array[@]}"
printf '%sn' "$result1" "$result2" "$result3"

相关内容

  • 没有找到相关文章

最新更新