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

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


当前回答

这是我的答案!

DELIMITER_VAL='='

read -d '' F_ABOUT_DISTRO_R <<"EOF"
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"
NAME="Ubuntu"
VERSION="14.04.4 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.4 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
EOF

SPLIT_NOW=$(awk -F$DELIMITER_VAL '{for(i=1;i<=NF;i++){printf "%s\n", $i}}' <<<"${F_ABOUT_DISTRO_R}")
while read -r line; do
   SPLIT+=("$line")
done <<< "$SPLIT_NOW"
for i in "${SPLIT[@]}"; do
    echo "$i"
done

为什么这种方法对我来说是“最好的”?

由于两个原因:

您不需要转义分隔符;空格不会有问题。该值将在数组中正确分隔。

其他回答

在Bash中,这是一种防弹的方式,即使您的变量包含换行符,也可以使用:

IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")

看:

$ in=$'one;two three;*;there is\na newline\nin this field'
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two three" [2]="*" [3]="there is
a newline
in this field")'

这项工作的诀窍是使用带有空分隔符的-d选项read(delimiter),这样read就被迫读取它所输入的所有内容。而且,由于printf,我们将read与中变量的内容完全匹配,没有换行符。注意,我们还在printf中放置分隔符,以确保传递给读取的字符串具有尾随分隔符。如果没有它,read将删除可能的尾随空字段:

$ in='one;two;three;'    # there's an empty field
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two" [2]="three" [3]="")'

保留后面的空字段。


Bash≥4.4的更新

从Bash 4.4开始,内置映射文件(也称为readarray)支持-d选项来指定分隔符。因此,另一种规范方法是:

mapfile -d ';' -t array < <(printf '%s;' "$in")

使用内置的集合加载$@数组:

IN="bla@some.com;john@home.com"
IFS=';'; set $IN; IFS=$' \t\n'

然后,让聚会开始:

echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2

又一个迟来的答案。。。如果你对java有兴趣,这里是bashj(https://sourceforge.net/projects/bashj/)解决方案:

#!/usr/bin/bashj

#!java

private static String[] cuts;
private static int cnt=0;
public static void split(String words,String regexp) {cuts=words.split(regexp);}
public static String next() {return(cnt<cuts.length ? cuts[cnt++] : "null");}

#!bash

IN="bla@some.com;john@home.com"

: j.split($IN,";")    # java method call

while true
do
    NAME=j.next()     # java method call
    if [ $NAME != null ] ; then echo $NAME ; else exit ; fi
done

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

如果你只想要前200。。。

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

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

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