我将此字符串存储在变量中:
IN="bla@some.com;john@home.com"
现在,我想将字符串拆分为:;分隔符,以便我可以:
ADDR1="bla@some.com"
ADDR2="john@home.com"
我不一定需要ADDR1和ADDR2变量。如果它们是数组的元素,那就更好了。
根据以下答案的建议,我最终得出了以下结论,这就是我想要的:
#!/usr/bin/env bash
IN="bla@some.com;john@home.com"
mails=$(echo $IN | tr ";" "\n")
for addr in $mails
do
echo "> [$addr]"
done
输出:
> [bla@some.com]
> [john@home.com]
有一个解决方案涉及将Internal_field_separator(IFS)设置为;。我不确定这个答案发生了什么,如何将IFS重置为默认值?
回复:IFS解决方案,我尝试了这个,它奏效了,我保留了旧的IFS,然后恢复它:
IN="bla@some.com;john@home.com"
OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
echo "> [$x]"
done
IFS=$OIFS
顺便说一下,当我尝试
mails2=($IN)
在循环中打印时,我只得到了第一个字符串,$in周围没有括号,它可以工作。
两种都不需要bash数组的伯尔尼式替代方案:
案例1:保持简洁:使用NewLine作为记录分隔符。。。如。
IN="bla@some.com
john@home.com"
while read i; do
# process "$i" ... eg.
echo "[email:$i]"
done <<< "$IN"
注意:在第一种情况下,没有分支子进程来帮助列表操作。
想法:也许值得在内部广泛使用NL,在外部生成最终结果时只转换为不同的RS。
案例2:使用“;”作为记录分隔符。。。如。
NL="
" IRS=";" ORS=";"
conv_IRS() {
exec tr "$1" "$NL"
}
conv_ORS() {
exec tr "$NL" "$1"
}
IN="bla@some.com;john@home.com"
IN="$(conv_IRS ";" <<< "$IN")"
while read i; do
# process "$i" ... eg.
echo -n "[email:$i]$ORS"
done <<< "$IN"
在这两种情况下,子列表都可以在循环中组成。循环完成后,子列表是持久的。这在处理内存中的列表时非常有用,而不是将列表存储在文件中。{请保持冷静,继续进行B-)}
除了已经提供的精彩答案之外,如果只是打印数据的问题,您可以考虑使用awk:
awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"
这将字段分隔符设置为;,以便它可以用for循环遍历字段并相应地打印。
Test
$ IN="bla@some.com;john@home.com"
$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"
> [bla@some.com]
> [john@home.com]
使用另一个输入:
$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;c d;e_;f"
> [a]
> [b]
> [c d]
> [e_]
> [f]
编辑:对不起,我在SO上读到POSIX需要perl,所以我认为使用它是合法的。但在unix.stackexchange.com上,一些用户表示perl不是POSIX规范的一部分。
我的解决方案:一个使用perl的拆分来完成工作的函数。
详细评论如下:
#!/bin/bash
# This function is a wrapper for Perl's split.\
# \
# Since we cannot return an array like in Perl,
# it takes the name of the resulting array as last
# argument.\
# \
# See https://perldoc.perl.org/functions/split for usage info
# and examples.\
# \
# If you provide a Perl regexp that contains e. g. an escaped token like \b,
# space(s) and/or capture group(s), it must be quoted, and e. g. /\b/ must
# be single-quoted.\
# Thus, it's best to generally single-quote a Perl regexp.
function split # Args: <Element separator regexp> <string> <array name>
{
(($# != 3)) && echo "${FUNCNAME[0]}: Wrong number of arguments, returning." && return 1
local elementSepRE=$1
local string=$2
local -n array=$3
local element i=0
# Attention! read does Word Splitting on each line!
# I must admit I didn't know that so far.
# This removes leading and trailing spaces, exactly
# what we don't want.
# Thus, we set IFS locally to newline only.
local IFS=$'\n'
while read element; do
# As opposed to array+=($element),
# this preserves leading and trailing spaces.
array[i++]=$element
done <<<$(_perl_split)
}
# This function calls Perl's split function and prints the elements of the
# resulting array on separate lines.\
# It uses the caller's $elementSepRE and $string.
function _perl_split
{
# A heredoc is a great way of embedding a Perl script.
# N.B.: - Shell variables get expanded.
# - Thus:
# - They must be quoted.
# - Perl scalar variables must be escaped.
# - The backslash of \n must be escaped to protect it.
# - Instead of redirecting a single heredoc to perl, we may
# use multiple heredocs with cat within a command group and
# pipe the result to perl.
# This enables us to conditionally add certain lines of code.
{
cat <<-END
my \$elementSepRE=q($elementSepRE);
END
# If $elementSepRE is a literal Perl regexp, qr must be applied
# to it in order to use it.
# N.B.: We cannot write this condition in Perl because when perl
# compiles the script, all statements are checked for validity,
# no matter if they will actually be executed or not.
# And if $elementSepRE was e. g. == ', the line below – although
# not to be executed – would give an error because of an unterminated
# single-quoted string.
[[ $elementSepRE =~ ^m?/ && $elementSepRE =~ /[msixpodualn]*$ ]] && cat <<-END
\$elementSepRE=qr$elementSepRE;
END
cat <<-END
my @array=split(\$elementSepRE, q($string));
print(\$_ . "\\n") for (@array);
END
} | perl
}
对于那些一眼就知道发生了什么的人来说,这一点没有任何评论;)
#!/bin/bash
# This function is a wrapper for Perl's split.\
# \
# Since we cannot return an array like in Perl,
# it takes the name of the resulting array as last
# argument.\
# \
# See https://perldoc.perl.org/functions/split for usage info
# and examples.\
# \
# If you provide a Perl regexp that contains e. g. an escaped token like \b,
# space(s) and/or capture group(s), it must be quoted, and e. g. /\b/ must
# be single-quoted.\
# Thus, it's best to generally single-quote a Perl regexp.
function split # Args: <Element separator regexp> <string> <array name>
{
(($# != 3)) && echo "${FUNCNAME[0]}: Wrong number of arguments, returning." && return 1
local elementSepRE=$1
local string=$2
local -n array=$3
local element i=0
local IFS=$'\n'
while read element; do
array[i++]=$element
done <<<$(_perl_split)
}
function _perl_split
{
{
cat <<-END
my \$elementSepRE=q($elementSepRE);
END
[[ $elementSepRE =~ ^m?/ && $elementSepRE =~ /[msixpodualn]*$ ]] && cat <<-END
\$elementSepRE=qr$elementSepRE;
END
cat <<-END
my @array=split(\$elementSepRE, q($string));
print(\$_ . "\\n") for (@array);
END
} | perl
}