我将此字符串存储在变量中:

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周围没有括号,它可以工作。


当前回答

编辑:对不起,我在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
}

其他回答

如果您不使用数组,那么这一行如何:

IFS=';' read ADDR1 ADDR2 <<<$IN

您可以设置内部字段分隔符(IFS)变量,然后让它解析为数组。当在命令中发生这种情况时,IFS的赋值只发生在单个命令的环境中(读取)。然后,它根据IFS变量值将输入解析为数组,然后我们可以对其进行迭代。

此示例将解析一行由;,将其推入阵列:

IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
  # process "$i"
done

另一个示例用于处理$IN的整个内容,每次一行输入由;:

while IFS=';' read -ra ADDR; do
  for i in "${ADDR[@]}"; do
    # process "$i"
  done
done <<< "$IN"

如果你只想要前200。。。

bash-4.2# for i in {1..200}; do echo $XXX | cut -d":" -f $i; done

您也可以编写类似“echo$PATH”之类的命令来代替“echo$SXX”。

*因为这是第31个答案,所以应该有一些理由:它的独特之处在于它使用了范围和切割

这里有一些很酷的答案(特别是勘误表),但对于类似于其他语言中的分裂的东西——这就是我最初的问题的意思——我决定了这个问题:

IN="bla@some.com;john@home.com"
declare -a a="(${IN/;/ })";

现在,${a[0]}、${a[1]}等都是您所期望的。使用${#a[*]}来表示术语的数量。或者迭代,当然:

for i in ${a[*]}; do echo $i; done

重要说明:

这在没有空间可担心的情况下有效,这解决了我的问题,但可能无法解决你的问题。在这种情况下,使用$IFS解决方案。

这种方法怎么样:

IN="bla@some.com;john@home.com" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

来源