将 Bash 数组转换为分隔字符串
Posted
技术标签:
【中文标题】将 Bash 数组转换为分隔字符串【英文标题】:Converting a Bash array into a delimited string 【发布时间】:2012-11-08 08:31:54 【问题描述】:我想知道以下内容;
-
为什么给定的无效示例不起作用。
如果有任何其他比工作示例中给出的更清洁的方法。
非工作示例
> 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 跳中实现变量替换,它永远不会工作......
相关:How can I join elements of an array in Bash?.
【参考方案1】:
因为括号用于分隔数组,而不是字符串:
ids="1 2 3 4";echo $ids// /|
1|2|3|4
一些示例:使用两个字符串填充 $ids
:a b
和 c 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'
字面意思是 space
、tabulation
和 (意思是或) 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 < <(...)
行将使用:
作为分隔符,不会永久设置$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
是一个更合适的名称,但与例如、Perl 和Python'sjoin
函数的工作原理一致?
@TheDudeAbides 无法直接命名 join
,因为这与连接文件行的现有命令名称冲突。
*facepalm* - 是的,当然。我忘了that。
不错的工具!允许可变长度分隔符!但考虑使用 nameref 将结果作为变量传递,以防止分叉...以上是关于将 Bash 数组转换为分隔字符串的主要内容,如果未能解决你的问题,请参考以下文章