如何在Bash中的分隔符上分割字符串?

时间:2022-03-27 03:47:30

I have this string stored in a variable:

我把这个字符串存储在一个变量中:

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

Now I would like to split the strings by ; delimiter so that I have:

现在我想把弦分开;分隔符,使我有:

ADDR1="bla@some.com"
ADDR2="john@home.com"

I don't necessarily need the ADDR1 and ADDR2 variables. If they are elements of an array that's even better.

我不需要ADDR1和ADDR2变量。如果它们是数组的元素,那就更好了。


After suggestions from the answers below, I ended up with the following which is what I was after:

从下面的答案中,我得到了以下几点:

#!/usr/bin/env bash

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

mails=$(echo $IN | tr ";" "\n")

for addr in $mails
do
    echo "> [$addr]"
done

Output:

输出:

> [bla@some.com]
> [john@home.com]

There was a solution involving setting Internal_field_separator (IFS) to ;. I am not sure what happened with that answer, how do you reset IFS back to default?

有一个解决方案,包括设置Internal_field_separator (IFS);我不知道那个答案是怎么回事,你怎么把IFS重新设置为默认值?

RE: IFS solution, I tried this and it works, I keep the old IFS and then restore it:

RE: IFS解决方案,我尝试了这个,它可以工作,我保留旧的IFS,然后恢复它:

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

OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
    echo "> [$x]"
done

IFS=$OIFS

BTW, when I tried

顺便说一下,当我试着

mails2=($IN)

I only got the first string when printing it in loop, without brackets around $IN it works.

当我在循环中打印它的时候,我只得到了第一个字符串,没有括号里面的$ in。

31 个解决方案

#1


878  

You can set the internal field separator (IFS) variable, and then let it parse into an array. When this happens in a command, then the assignment to IFS only takes place to that single command's environment (to read ). It then parses the input according to the IFS variable value into an array, which we can then iterate over.

您可以设置内部字段分隔符(IFS)变量,然后让它解析为一个数组。当这发生在一个命令中,那么对IFS的赋值只发生在单个命令的环境中(读取)。然后,它根据IFS变量值将输入解析为一个数组,然后我们可以遍历它。

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

It will parse one line of items separated by ;, pushing it into an array. Stuff for processing whole of $IN, each time one line of input separated by ;:

它将解析与之分隔的一行,并将其推入数组中。用于处理整个$IN的东西,每次一行输入由;

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

#2


713  

Taken from Bash shell script split array:

从Bash shell脚本拆分数组:

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

Explanation:

解释:

This construction replaces all occurrences of ';' (the initial // means global replace) in the string IN with ' ' (a single space), then interprets the space-delimited string as an array (that's what the surrounding parentheses do).

这个构造替换了字符串中所有出现的“;”(初始//意味着全局替换),然后用“”(一个空格),然后将空格分隔的字符串解释为一个数组(这就是周围的括号所做的)。

The syntax used inside of the curly braces to replace each ';' character with a ' ' character is called Parameter Expansion.

在大括号内使用的语法来替换每个“;”字符与一个“字符”被称为参数扩展。

There are some common gotchas:

有一些常见的问题:

  1. If the original string has spaces, you will need to use IFS:
    • IFS=':'; arrIN=($IN); unset IFS;
    • IFS = ':';arrIN =(美元);设置IFS;
  2. 如果原始字符串有空格,则需要使用IFS: IFS=':;arrIN =(美元);设置IFS;
  3. If the original string has spaces and the delimiter is a new line, you can set IFS with:
    • IFS=$'\n'; arrIN=($IN); unset IFS;
    • IFS = $ ' \ n ';arrIN =(美元);设置IFS;
  4. 如果原始字符串有空格,而分隔符是一个新行,则可以使用:IFS=$“\n”来设置IFS;arrIN =(美元);设置IFS;

#3


205  

If you don't mind processing them immediately, I like to do this:

如果你不介意马上处理它们,我喜欢这样做:

for i in $(echo $IN | tr ";" "\n")
do
  # process
done

You could use this kind of loop to initialize an array, but there's probably an easier way to do it. Hope this helps, though.

您可以使用这种循环来初始化一个数组,但是可能有更简单的方法。希望这可以帮助。

#4


112  

Compatible answer

To this SO question, there is already a lot of different way to do this in . But bash has many special features, so called bashism that work well, but that won't work in any other .

对于这个问题,在bash中已经有很多不同的方法。但是bash有许多特殊的特性,所以称为bashism,它工作得很好,但是在其他shell中都不起作用。

In particular, arrays, associative array, and pattern substitution are pure bashisms and may not work under other shells.

具体来说,数组、关联数组和模式替换都是纯粹的bashisms,在其他shell中可能不起作用。

On my Debian GNU/Linux, there is a standard shell called , but I know many people who like to use .

在我的Debian GNU/Linux上,有一个标准的shell叫做dash,但是我知道很多人喜欢使用ksh。

Finally, in very small situation, there is a special tool called with his own shell interpreter ().

最后,在非常小的情况下,有一个特殊的工具叫做busybox,它有自己的shell解释器(ash)。

Requested string

The string sample in SO question is:

所以问题的字符串样本是:

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

As this could be useful with whitespaces and as whitespaces could modify the result of the routine, I prefer to use this sample string:

由于这可能对whitespaces有用,而且由于whitespaces可以修改例程的结果,所以我更喜欢使用这个示例字符串:

 IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

Split string based on delimiter in (version >=4.2)

Under pure bash, we may use arrays and IFS:

在纯bash中,我们可以使用数组和IFS:

var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS

IFS=\; read -a fields <<<"$var"

Using this syntax under recent bash don't change $IFS for current session, but only for the current command:

在最近的bash中使用此语法不会为当前会话更改$IFS,但只针对当前命令:

set | grep ^IFS=
IFS=$' \t\n'

Now the string var is split and stored into an array (named fields):

现在,字符串var被拆分并存储到一个数组中(命名字段):

set | grep ^fields=\\\|^var=
fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
var='bla@some.com;john@home.com;Full Name <fulnam@other.org>'

We could request for variable content with declare -p:

我们可以要求使用声明-p的变量内容:

declare -p var fields
declare -- var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")

read is the quickiest way to do the split, because there is no forks and no external resources called.

阅读是进行拆分的最快捷的方式,因为没有分支,也没有调用外部资源。

From there, you could use the syntax you already know for processing each field:

从那里,您可以使用您已经知道的语法来处理每个字段:

for x in "${fields[@]}";do
    echo "> [$x]"
    done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]

or drop each field after processing (I like this shifting approach):

或者在处理后删除每个字段(我喜欢这种转换方法):

while [ "$fields" ] ;do
    echo "> [$fields]"
    fields=("${fields[@]:1}")
    done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]

or even for simple printout (shorter syntax):

甚至是简单的打印输出(更短的语法):

printf "> [%s]\n" "${fields[@]}"
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]

Split string based on delimiter in

But if you would write something usable under many shells, you have to not use bashisms.

但是,如果你要在许多shell下写一些可用的东西,你就不能使用bashisms。

There is a syntax, used in many shells, for splitting a string across first or last occurrence of a substring:

在许多shell中使用了一种语法,用于在子字符串的第一次或最后一次发生时拆分字符串:

${var#*SubStr}  # will drop begin of string up to first occur of `SubStr`
${var##*SubStr} # will drop begin of string up to last occur of `SubStr`
${var%SubStr*}  # will drop part of string from last occur of `SubStr` to the end
${var%%SubStr*} # will drop part of string from first occur of `SubStr` to the end

(The missing of this is the main reason of my answer publication ;)

(这是我的答案发表的主要原因;)

As pointed out by Score_Under:

正如Score_Under所指出的:

# and % delete the shortest possible matching string, and

#和%删除尽可能短的匹配字符串。

## and %% delete the longest possible.

##和%%删除尽可能长的时间。

This little sample script work well under , , , and was tested under Mac-OS's bash too:

这个示例脚本在bash、dash、ksh、busybox下很好地工作,并在macos的bash下进行了测试:

var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
while [ "$var" ] ;do
    iter=${var%%;*}
    echo "> [$iter]"
    [ "$var" = "$iter" ] && \
        var='' || \
        var="${var#*;}"
  done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]

Have fun!

玩得开心!

#5


80  

How about this approach:

这个方法:

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

Source

#6


65  

I've seen a couple of answers referencing the cut command, but they've all been deleted. It's a little odd that nobody has elaborated on that, because I think it's one of the more useful commands for doing this type of thing, especially for parsing delimited log files.

我已经看到了一些引用cut命令的答案,但是它们都被删除了。这有点奇怪,因为我认为这是做这类事情的一个更有用的命令,尤其是解析分隔的日志文件。

In the case of splitting this specific example into a bash script array, tr is probably more efficient, but cut can be used, and is more effective if you want to pull specific fields from the middle.

在将这个特定的示例拆分为bash脚本数组的情况下,tr可能更有效,但是可以使用cut,而且如果您想从中间提取特定字段,则更有效。

Example:

例子:

$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

You can obviously put that into a loop, and iterate the -f parameter to pull each field independently.

您可以很明显地将其放入一个循环中,并迭代-f参数以独立地拉动每个字段。

This gets more useful when you have a delimited log file with rows like this:

当使用这样的行进行分隔的日志文件时,这将变得更有用:

2015-04-27|12345|some action|an attribute|meta data

cut is very handy to be able to cat this file and select a particular field for further processing.

剪切是非常方便的,可以选择这个文件,并选择一个特定的字段进行进一步处理。

#7


57  

echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com

#8


57  

This worked for me:

这工作对我来说:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2

#9


56  

This also works:

这同样适用:

IN="bla@some.com;john@home.com"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Be careful, this solution is not always correct. In case you pass "bla@some.com" only, it will assign it to both ADD1 and ADD2.

要小心,这个解决方案并不总是正确的。如果你只通过“bla@some.com”,它会把它分配给ADD1和ADD2。

#10


32  

I think AWK is the best and efficient command to resolve your problem. AWK is included in Bash by default in almost every Linux distribution.

我认为AWK是解决您的问题的最佳和有效的命令。在几乎所有的Linux发行版中,AWK都默认包含在Bash中。

echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'

will give

将会给

bla@some.com john@home.com

Of course your can store each email address by redefining the awk print field.

当然,您可以通过重新定义awk打印字段来存储每个电子邮件地址。

#11


25  

A different take on Darron's answer, this is how I do it:

对达隆的回答有不同的看法,我就是这么做的:

IN="bla@some.com;john@home.com"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)

#12


23  

In Bash, a bullet proof way, that will work even if your variable contains newlines:

在Bash中,一种防弹方法,即使你的变量包含换行符,它也能起作用:

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

Look:

看:

$ 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")'

The trick for this to work is to use the -d option of read (delimiter) with an empty delimiter, so that read is forced to read everything it's fed. And we feed read with exactly the content of the variable in, with no trailing newline thanks to printf. Note that's we're also putting the delimiter in printf to ensure that the string passed to read has a trailing delimiter. Without it, read would trim potential trailing empty fields:

这个操作的诀窍是使用一个空的分隔符读取(分隔符)的-d选项,这样读就不得不读取它所提供的所有内容。我们还会以完全正确的变量的内容来读,因为printf没有拖尾的新行。注意,我们还将分隔符放在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]="")'

the trailing empty field is preserved.

后面的空字段被保留。


Update for Bash≥4.4

Since Bash 4.4, the builtin mapfile (aka readarray) supports the -d option to specify a delimiter. Hence another canonical way is:

从Bash 4.4开始,builtin mapfile(即readarray)支持-d选项来指定分隔符。因此,另一种规范的方法是:

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

#13


17  

How about this one liner, if you're not using arrays:

如果你不使用数组的话,这条线怎么样?

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

#14


15  

Without setting the IFS

没有设置IFS

If you just have one colon you can do that:

如果你只有一个结肠,你可以这样做:

a="foo:bar"
b=${a%:*}
c=${a##*:}

you will get:

你将得到:

b = foo
c = bar

#15


13  

Here is a clean 3-liner:

这是一款干净的3-班轮:

in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done

where IFS delimit words based on the separator and () is used to create an array. Then [@] is used to return each item as a separate word.

如果IFS将基于分隔符和()的单词分隔开,则用于创建一个数组。然后,[@]用于将每个项目作为单独的单词返回。

If you've any code after that, you also need to restore $IFS, e.g. unset IFS.

如果您之后有任何代码,您也需要恢复$IFS,例如unset IFS。

#16


5  

There is a simple and smart way like this:

有一个简单而聪明的方法:

echo "add:sfff" | xargs -d: -i  echo {}

But you must use gnu xargs, BSD xargs cant support -d delim. If you use apple mac like me. You can install gnu xargs :

但是您必须使用gnu xargs, BSD xargs不能支持-d delim。如果你像我一样使用苹果电脑。您可以安装gnu xargs:

brew install findutils

then

然后

echo "add:sfff" | gxargs -d: -i  echo {}

#17


4  

This is the simplest way to do it.

这是最简单的方法。

spo='one;two;three'
OIFS=$IFS
IFS=';'
spo_array=($spo)
IFS=$OIFS
echo ${spo_array[*]}

#18


4  

The following Bash/zsh function splits its first argument on the delimiter given by the second argument:

下面的Bash/zsh函数将第一个参数拆分为第二个参数给出的分隔符:

split() {
    local string="$1"
    local delimiter="$2"
    if [ -n "$string" ]; then
        local part
        while read -d "$delimiter" part; do
            echo $part
        done <<< "$string"
        echo $part
    fi
}

For instance, the command

例如,命令

$ split 'a;b;c' ';'

yields

收益率

a
b
c

This output may, for instance, be piped to other commands. Example:

例如,这个输出可以被传输到其他命令。例子:

$ split 'a;b;c' ';' | cat -n
1   a
2   b
3   c

Compared to the other solutions given, this one has the following advantages:

与其他方案相比,这一方案具有以下优点:

  • IFS is not overriden: Due to dynamic scoping of even local variables, overriding IFS over a loop causes the new value to leak into function calls performed from within the loop.

    IFS并不是overriden:由于甚至局部变量的动态范围,在循环中覆盖IFS会使新值从循环中执行到函数调用中。

  • Arrays are not used: Reading a string into an array using read requires the flag -a in Bash and -A in zsh.

    数组不被使用:使用read将字符串读入数组中需要在Bash中标记为a,在zsh中为-a。

If desired, the function may be put into a script as follows:

如果需要,可以将该函数放入脚本中,如下所示:

#!/usr/bin/env bash

split() {
    # ...
}

split "$@"

#19


3  

IN="bla@some.com;john@home.com"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

Output

输出

bla@some.com
john@home.com

System : Ubuntu 12.04.1

系统:Ubuntu 12.04.1

#20


2  

If no space, Why not this?

如果没有空间,为什么不呢?

IN="bla@some.com;john@home.com"
arr=(`echo $IN | tr ';' ' '`)

echo ${arr[0]}
echo ${arr[1]}

#21


2  

There are some cool answers here (errator esp.), but for something analogous to split in other languages -- which is what I took the original question to mean -- I settled on this:

这里有一些很酷的答案(errator esp.),但是对于一些类似于其他语言的分裂的东西——这是我最初提出的问题——我解决了这个问题:

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

Now ${a[0]}, ${a[1]}, etc, are as you would expect. Use ${#a[*]} for number of terms. Or to iterate, of course:

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

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

IMPORTANT NOTE:

重要提示:

This works in cases where there are no spaces to worry about, which solved my problem, but may not solve yours. Go with the $IFS solution(s) in that case.

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

#22


1  

Use the set built-in to load up the $@ array:

使用内置的设置来加载$@数组:

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

Then, let the party begin:

然后,让聚会开始:

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

#23


1  

Two bourne-ish alternatives where neither require bash arrays:

两个不需要bash数组的bourne-ish方法:

Case 1: Keep it nice and simple: Use a NewLine as the Record-Separator... eg.

案例1:保持简洁:使用换行符作为记录分隔符…如。

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

while read i; do
  # process "$i" ... eg.
    echo "[email:$i]"
done <<< "$IN"

Note: in this first case no sub-process is forked to assist with list manipulation.

注意:在第一个案例中,没有一个子过程被用来辅助列表操作。

Idea: Maybe it is worth using NL extensively internally, and only converting to a different RS when generating the final result externally.

想法:也许值得在内部广泛使用NL,并且只在外部生成最终结果时转换为不同的RS。

Case 2: Using a ";" as a record separator... eg.

案例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"

In both cases a sub-list can be composed within the loop is persistent after the loop has completed. This is useful when manipulating lists in memory, instead storing lists in files. {p.s. keep calm and carry on B-) }

在这两种情况下,子列表都可以在循环完成后,在循环中进行持久化。当在内存中操作列表时,这很有用,而是将列表存储在文件中。{注。保持冷静,进行B-)}

#24


1  

Apart from the fantastic answers that were already provided, if it is just a matter of printing out the data you may consider using awk:

除了已经提供的精彩答案之外,如果只是打印出你可以考虑使用awk的数据:

awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"

This sets the field separator to ;, so that it can loop through the fields with a for loop and print accordingly.

这将字段分隔符设置为;,以便它可以循环遍历字段,并相应地打印一个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]

With another input:

与另一个输入:

$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;c   d;e_;f"
> [a]
> [b]
> [c   d]
> [e_]
> [f]

#25


1  

In Android shell, most of the proposed methods just do not work:

在Android shell中,大多数建议的方法都不起作用:

$ IFS=':' read -ra ADDR <<<"$PATH"                             
/system/bin/sh: can't create temporary file /sqlite_stmt_journals/mksh.EbNoR10629: No such file or directory

What does work is:

什么工作是:

$ for i in ${PATH//:/ }; do echo $i; done
/sbin
/vendor/bin
/system/sbin
/system/bin
/system/xbin

where // means global replacement.

在哪里//意味着全球替代。

#26


0  

A one-liner to split a string separated by ';' into an array is:

将一个字符串分隔成一个数组的一行程序是:

IN="bla@some.com;john@home.com"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

This only sets IFS in a subshell, so you don't have to worry about saving and restoring its value.

这只在子shell中设置IFS,所以您不必担心保存和恢复它的值。

#27


0  

IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
set -f
oldifs="$IFS"
IFS=';'; arrayIN=($IN)
IFS="$oldifs"
for i in "${arrayIN[@]}"; do
echo "$i"
done
set +f

Output:

输出:

bla@some.com
john@home.com
Charlie Brown <cbrown@acme.com
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)

Explanation: Simple assignment using parenthesis () converts semicolon separated list into an array provided you have correct IFS while doing that. Standard FOR loop handles individual items in that array as usual. Notice that the list given for IN variable must be "hard" quoted, that is, with single ticks.

说明:使用圆括号()的简单赋值将分号分隔的列表转换为一个数组,前提是在执行该操作时,您有正确的IFS。循环的标准像往常一样处理该数组中的各个项。请注意,在变量中给出的列表必须是“硬”引号,即单引号。

IFS must be saved and restored since Bash does not treat an assignment the same way as a command. An alternate workaround is to wrap the assignment inside a function and call that function with a modified IFS. In that case separate saving/restoring of IFS is not needed. Thanks for "Bize" for pointing that out.

由于Bash不像命令那样对待赋值,因此必须保存和恢复IFS。另一种变通方法是将赋值封装到函数中,并使用一个已修改的IFS调用该函数。在这种情况下,不需要单独保存或恢复IFS。感谢“Bize”指出这一点。

#28


0  

Maybe not the most elegant solution, but works with * and spaces:

也许不是最优雅的解决方案,但可以使用*和空格:

IN="bla@so me.com;*;john@home.com"
for i in `delims=${IN//[^;]}; seq 1 $((${#delims} + 1))`
do
   echo "> [`echo $IN | cut -d';' -f$i`]"
done

Outputs

输出

> [bla@so me.com]
> [*]
> [john@home.com]

Other example (delimiters at beginning and end):

其他例子(开始和结束的分隔符):

IN=";bla@so me.com;*;john@home.com;"
> []
> [bla@so me.com]
> [*]
> [john@home.com]
> []

Basically it removes every character other than ; making delims eg. ;;;. Then it does for loop from 1 to number-of-delimiters as counted by ${#delims}. The final step is to safely get the $ith part using cut.

基本上它除去了所有的字符;使delims如。;;;。然后,从1到数字分隔符循环,按${#delims}计算。最后一步是使用cut来安全地获得第i个部分。

#29


0  

Okay guys!

好,伙计们!

Here's my answer!

这是我的答案!

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

Why this approach is "the best" for me?

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

Because of two reasons:

因为两个原因:

  1. You do not need to escape the delimiter;
  2. 你不需要逃离分隔符;
  3. You will not have problem with blank spaces. The value will be properly separated in the array!
  4. 你不会对空格有问题。该值将在数组中正确地分离!

[]'s

[]的

#30


0  

you can apply awk to many situations

你可以在很多情况下使用awk。

echo "bla@some.com;john@home.com"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

also you can use this

你也可以用这个。

echo "bla@some.com;john@home.com"|awk -F';' '{print $1,$2}' OFS="\n"

#1


878  

You can set the internal field separator (IFS) variable, and then let it parse into an array. When this happens in a command, then the assignment to IFS only takes place to that single command's environment (to read ). It then parses the input according to the IFS variable value into an array, which we can then iterate over.

您可以设置内部字段分隔符(IFS)变量,然后让它解析为一个数组。当这发生在一个命令中,那么对IFS的赋值只发生在单个命令的环境中(读取)。然后,它根据IFS变量值将输入解析为一个数组,然后我们可以遍历它。

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

It will parse one line of items separated by ;, pushing it into an array. Stuff for processing whole of $IN, each time one line of input separated by ;:

它将解析与之分隔的一行,并将其推入数组中。用于处理整个$IN的东西,每次一行输入由;

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

#2


713  

Taken from Bash shell script split array:

从Bash shell脚本拆分数组:

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

Explanation:

解释:

This construction replaces all occurrences of ';' (the initial // means global replace) in the string IN with ' ' (a single space), then interprets the space-delimited string as an array (that's what the surrounding parentheses do).

这个构造替换了字符串中所有出现的“;”(初始//意味着全局替换),然后用“”(一个空格),然后将空格分隔的字符串解释为一个数组(这就是周围的括号所做的)。

The syntax used inside of the curly braces to replace each ';' character with a ' ' character is called Parameter Expansion.

在大括号内使用的语法来替换每个“;”字符与一个“字符”被称为参数扩展。

There are some common gotchas:

有一些常见的问题:

  1. If the original string has spaces, you will need to use IFS:
    • IFS=':'; arrIN=($IN); unset IFS;
    • IFS = ':';arrIN =(美元);设置IFS;
  2. 如果原始字符串有空格,则需要使用IFS: IFS=':;arrIN =(美元);设置IFS;
  3. If the original string has spaces and the delimiter is a new line, you can set IFS with:
    • IFS=$'\n'; arrIN=($IN); unset IFS;
    • IFS = $ ' \ n ';arrIN =(美元);设置IFS;
  4. 如果原始字符串有空格,而分隔符是一个新行,则可以使用:IFS=$“\n”来设置IFS;arrIN =(美元);设置IFS;

#3


205  

If you don't mind processing them immediately, I like to do this:

如果你不介意马上处理它们,我喜欢这样做:

for i in $(echo $IN | tr ";" "\n")
do
  # process
done

You could use this kind of loop to initialize an array, but there's probably an easier way to do it. Hope this helps, though.

您可以使用这种循环来初始化一个数组,但是可能有更简单的方法。希望这可以帮助。

#4


112  

Compatible answer

To this SO question, there is already a lot of different way to do this in . But bash has many special features, so called bashism that work well, but that won't work in any other .

对于这个问题,在bash中已经有很多不同的方法。但是bash有许多特殊的特性,所以称为bashism,它工作得很好,但是在其他shell中都不起作用。

In particular, arrays, associative array, and pattern substitution are pure bashisms and may not work under other shells.

具体来说,数组、关联数组和模式替换都是纯粹的bashisms,在其他shell中可能不起作用。

On my Debian GNU/Linux, there is a standard shell called , but I know many people who like to use .

在我的Debian GNU/Linux上,有一个标准的shell叫做dash,但是我知道很多人喜欢使用ksh。

Finally, in very small situation, there is a special tool called with his own shell interpreter ().

最后,在非常小的情况下,有一个特殊的工具叫做busybox,它有自己的shell解释器(ash)。

Requested string

The string sample in SO question is:

所以问题的字符串样本是:

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

As this could be useful with whitespaces and as whitespaces could modify the result of the routine, I prefer to use this sample string:

由于这可能对whitespaces有用,而且由于whitespaces可以修改例程的结果,所以我更喜欢使用这个示例字符串:

 IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

Split string based on delimiter in (version >=4.2)

Under pure bash, we may use arrays and IFS:

在纯bash中,我们可以使用数组和IFS:

var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS

IFS=\; read -a fields <<<"$var"

Using this syntax under recent bash don't change $IFS for current session, but only for the current command:

在最近的bash中使用此语法不会为当前会话更改$IFS,但只针对当前命令:

set | grep ^IFS=
IFS=$' \t\n'

Now the string var is split and stored into an array (named fields):

现在,字符串var被拆分并存储到一个数组中(命名字段):

set | grep ^fields=\\\|^var=
fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
var='bla@some.com;john@home.com;Full Name <fulnam@other.org>'

We could request for variable content with declare -p:

我们可以要求使用声明-p的变量内容:

declare -p var fields
declare -- var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")

read is the quickiest way to do the split, because there is no forks and no external resources called.

阅读是进行拆分的最快捷的方式,因为没有分支,也没有调用外部资源。

From there, you could use the syntax you already know for processing each field:

从那里,您可以使用您已经知道的语法来处理每个字段:

for x in "${fields[@]}";do
    echo "> [$x]"
    done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]

or drop each field after processing (I like this shifting approach):

或者在处理后删除每个字段(我喜欢这种转换方法):

while [ "$fields" ] ;do
    echo "> [$fields]"
    fields=("${fields[@]:1}")
    done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]

or even for simple printout (shorter syntax):

甚至是简单的打印输出(更短的语法):

printf "> [%s]\n" "${fields[@]}"
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]

Split string based on delimiter in

But if you would write something usable under many shells, you have to not use bashisms.

但是,如果你要在许多shell下写一些可用的东西,你就不能使用bashisms。

There is a syntax, used in many shells, for splitting a string across first or last occurrence of a substring:

在许多shell中使用了一种语法,用于在子字符串的第一次或最后一次发生时拆分字符串:

${var#*SubStr}  # will drop begin of string up to first occur of `SubStr`
${var##*SubStr} # will drop begin of string up to last occur of `SubStr`
${var%SubStr*}  # will drop part of string from last occur of `SubStr` to the end
${var%%SubStr*} # will drop part of string from first occur of `SubStr` to the end

(The missing of this is the main reason of my answer publication ;)

(这是我的答案发表的主要原因;)

As pointed out by Score_Under:

正如Score_Under所指出的:

# and % delete the shortest possible matching string, and

#和%删除尽可能短的匹配字符串。

## and %% delete the longest possible.

##和%%删除尽可能长的时间。

This little sample script work well under , , , and was tested under Mac-OS's bash too:

这个示例脚本在bash、dash、ksh、busybox下很好地工作,并在macos的bash下进行了测试:

var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
while [ "$var" ] ;do
    iter=${var%%;*}
    echo "> [$iter]"
    [ "$var" = "$iter" ] && \
        var='' || \
        var="${var#*;}"
  done
> [bla@some.com]
> [john@home.com]
> [Full Name <fulnam@other.org>]

Have fun!

玩得开心!

#5


80  

How about this approach:

这个方法:

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

Source

#6


65  

I've seen a couple of answers referencing the cut command, but they've all been deleted. It's a little odd that nobody has elaborated on that, because I think it's one of the more useful commands for doing this type of thing, especially for parsing delimited log files.

我已经看到了一些引用cut命令的答案,但是它们都被删除了。这有点奇怪,因为我认为这是做这类事情的一个更有用的命令,尤其是解析分隔的日志文件。

In the case of splitting this specific example into a bash script array, tr is probably more efficient, but cut can be used, and is more effective if you want to pull specific fields from the middle.

在将这个特定的示例拆分为bash脚本数组的情况下,tr可能更有效,但是可以使用cut,而且如果您想从中间提取特定字段,则更有效。

Example:

例子:

$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

You can obviously put that into a loop, and iterate the -f parameter to pull each field independently.

您可以很明显地将其放入一个循环中,并迭代-f参数以独立地拉动每个字段。

This gets more useful when you have a delimited log file with rows like this:

当使用这样的行进行分隔的日志文件时,这将变得更有用:

2015-04-27|12345|some action|an attribute|meta data

cut is very handy to be able to cat this file and select a particular field for further processing.

剪切是非常方便的,可以选择这个文件,并选择一个特定的字段进行进一步处理。

#7


57  

echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com

#8


57  

This worked for me:

这工作对我来说:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2

#9


56  

This also works:

这同样适用:

IN="bla@some.com;john@home.com"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Be careful, this solution is not always correct. In case you pass "bla@some.com" only, it will assign it to both ADD1 and ADD2.

要小心,这个解决方案并不总是正确的。如果你只通过“bla@some.com”,它会把它分配给ADD1和ADD2。

#10


32  

I think AWK is the best and efficient command to resolve your problem. AWK is included in Bash by default in almost every Linux distribution.

我认为AWK是解决您的问题的最佳和有效的命令。在几乎所有的Linux发行版中,AWK都默认包含在Bash中。

echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'

will give

将会给

bla@some.com john@home.com

Of course your can store each email address by redefining the awk print field.

当然,您可以通过重新定义awk打印字段来存储每个电子邮件地址。

#11


25  

A different take on Darron's answer, this is how I do it:

对达隆的回答有不同的看法,我就是这么做的:

IN="bla@some.com;john@home.com"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)

#12


23  

In Bash, a bullet proof way, that will work even if your variable contains newlines:

在Bash中,一种防弹方法,即使你的变量包含换行符,它也能起作用:

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

Look:

看:

$ 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")'

The trick for this to work is to use the -d option of read (delimiter) with an empty delimiter, so that read is forced to read everything it's fed. And we feed read with exactly the content of the variable in, with no trailing newline thanks to printf. Note that's we're also putting the delimiter in printf to ensure that the string passed to read has a trailing delimiter. Without it, read would trim potential trailing empty fields:

这个操作的诀窍是使用一个空的分隔符读取(分隔符)的-d选项,这样读就不得不读取它所提供的所有内容。我们还会以完全正确的变量的内容来读,因为printf没有拖尾的新行。注意,我们还将分隔符放在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]="")'

the trailing empty field is preserved.

后面的空字段被保留。


Update for Bash≥4.4

Since Bash 4.4, the builtin mapfile (aka readarray) supports the -d option to specify a delimiter. Hence another canonical way is:

从Bash 4.4开始,builtin mapfile(即readarray)支持-d选项来指定分隔符。因此,另一种规范的方法是:

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

#13


17  

How about this one liner, if you're not using arrays:

如果你不使用数组的话,这条线怎么样?

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

#14


15  

Without setting the IFS

没有设置IFS

If you just have one colon you can do that:

如果你只有一个结肠,你可以这样做:

a="foo:bar"
b=${a%:*}
c=${a##*:}

you will get:

你将得到:

b = foo
c = bar

#15


13  

Here is a clean 3-liner:

这是一款干净的3-班轮:

in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done

where IFS delimit words based on the separator and () is used to create an array. Then [@] is used to return each item as a separate word.

如果IFS将基于分隔符和()的单词分隔开,则用于创建一个数组。然后,[@]用于将每个项目作为单独的单词返回。

If you've any code after that, you also need to restore $IFS, e.g. unset IFS.

如果您之后有任何代码,您也需要恢复$IFS,例如unset IFS。

#16


5  

There is a simple and smart way like this:

有一个简单而聪明的方法:

echo "add:sfff" | xargs -d: -i  echo {}

But you must use gnu xargs, BSD xargs cant support -d delim. If you use apple mac like me. You can install gnu xargs :

但是您必须使用gnu xargs, BSD xargs不能支持-d delim。如果你像我一样使用苹果电脑。您可以安装gnu xargs:

brew install findutils

then

然后

echo "add:sfff" | gxargs -d: -i  echo {}

#17


4  

This is the simplest way to do it.

这是最简单的方法。

spo='one;two;three'
OIFS=$IFS
IFS=';'
spo_array=($spo)
IFS=$OIFS
echo ${spo_array[*]}

#18


4  

The following Bash/zsh function splits its first argument on the delimiter given by the second argument:

下面的Bash/zsh函数将第一个参数拆分为第二个参数给出的分隔符:

split() {
    local string="$1"
    local delimiter="$2"
    if [ -n "$string" ]; then
        local part
        while read -d "$delimiter" part; do
            echo $part
        done <<< "$string"
        echo $part
    fi
}

For instance, the command

例如,命令

$ split 'a;b;c' ';'

yields

收益率

a
b
c

This output may, for instance, be piped to other commands. Example:

例如,这个输出可以被传输到其他命令。例子:

$ split 'a;b;c' ';' | cat -n
1   a
2   b
3   c

Compared to the other solutions given, this one has the following advantages:

与其他方案相比,这一方案具有以下优点:

  • IFS is not overriden: Due to dynamic scoping of even local variables, overriding IFS over a loop causes the new value to leak into function calls performed from within the loop.

    IFS并不是overriden:由于甚至局部变量的动态范围,在循环中覆盖IFS会使新值从循环中执行到函数调用中。

  • Arrays are not used: Reading a string into an array using read requires the flag -a in Bash and -A in zsh.

    数组不被使用:使用read将字符串读入数组中需要在Bash中标记为a,在zsh中为-a。

If desired, the function may be put into a script as follows:

如果需要,可以将该函数放入脚本中,如下所示:

#!/usr/bin/env bash

split() {
    # ...
}

split "$@"

#19


3  

IN="bla@some.com;john@home.com"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

Output

输出

bla@some.com
john@home.com

System : Ubuntu 12.04.1

系统:Ubuntu 12.04.1

#20


2  

If no space, Why not this?

如果没有空间,为什么不呢?

IN="bla@some.com;john@home.com"
arr=(`echo $IN | tr ';' ' '`)

echo ${arr[0]}
echo ${arr[1]}

#21


2  

There are some cool answers here (errator esp.), but for something analogous to split in other languages -- which is what I took the original question to mean -- I settled on this:

这里有一些很酷的答案(errator esp.),但是对于一些类似于其他语言的分裂的东西——这是我最初提出的问题——我解决了这个问题:

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

Now ${a[0]}, ${a[1]}, etc, are as you would expect. Use ${#a[*]} for number of terms. Or to iterate, of course:

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

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

IMPORTANT NOTE:

重要提示:

This works in cases where there are no spaces to worry about, which solved my problem, but may not solve yours. Go with the $IFS solution(s) in that case.

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

#22


1  

Use the set built-in to load up the $@ array:

使用内置的设置来加载$@数组:

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

Then, let the party begin:

然后,让聚会开始:

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

#23


1  

Two bourne-ish alternatives where neither require bash arrays:

两个不需要bash数组的bourne-ish方法:

Case 1: Keep it nice and simple: Use a NewLine as the Record-Separator... eg.

案例1:保持简洁:使用换行符作为记录分隔符…如。

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

while read i; do
  # process "$i" ... eg.
    echo "[email:$i]"
done <<< "$IN"

Note: in this first case no sub-process is forked to assist with list manipulation.

注意:在第一个案例中,没有一个子过程被用来辅助列表操作。

Idea: Maybe it is worth using NL extensively internally, and only converting to a different RS when generating the final result externally.

想法:也许值得在内部广泛使用NL,并且只在外部生成最终结果时转换为不同的RS。

Case 2: Using a ";" as a record separator... eg.

案例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"

In both cases a sub-list can be composed within the loop is persistent after the loop has completed. This is useful when manipulating lists in memory, instead storing lists in files. {p.s. keep calm and carry on B-) }

在这两种情况下,子列表都可以在循环完成后,在循环中进行持久化。当在内存中操作列表时,这很有用,而是将列表存储在文件中。{注。保持冷静,进行B-)}

#24


1  

Apart from the fantastic answers that were already provided, if it is just a matter of printing out the data you may consider using awk:

除了已经提供的精彩答案之外,如果只是打印出你可以考虑使用awk的数据:

awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"

This sets the field separator to ;, so that it can loop through the fields with a for loop and print accordingly.

这将字段分隔符设置为;,以便它可以循环遍历字段,并相应地打印一个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]

With another input:

与另一个输入:

$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;c   d;e_;f"
> [a]
> [b]
> [c   d]
> [e_]
> [f]

#25


1  

In Android shell, most of the proposed methods just do not work:

在Android shell中,大多数建议的方法都不起作用:

$ IFS=':' read -ra ADDR <<<"$PATH"                             
/system/bin/sh: can't create temporary file /sqlite_stmt_journals/mksh.EbNoR10629: No such file or directory

What does work is:

什么工作是:

$ for i in ${PATH//:/ }; do echo $i; done
/sbin
/vendor/bin
/system/sbin
/system/bin
/system/xbin

where // means global replacement.

在哪里//意味着全球替代。

#26


0  

A one-liner to split a string separated by ';' into an array is:

将一个字符串分隔成一个数组的一行程序是:

IN="bla@some.com;john@home.com"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

This only sets IFS in a subshell, so you don't have to worry about saving and restoring its value.

这只在子shell中设置IFS,所以您不必担心保存和恢复它的值。

#27


0  

IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
set -f
oldifs="$IFS"
IFS=';'; arrayIN=($IN)
IFS="$oldifs"
for i in "${arrayIN[@]}"; do
echo "$i"
done
set +f

Output:

输出:

bla@some.com
john@home.com
Charlie Brown <cbrown@acme.com
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)

Explanation: Simple assignment using parenthesis () converts semicolon separated list into an array provided you have correct IFS while doing that. Standard FOR loop handles individual items in that array as usual. Notice that the list given for IN variable must be "hard" quoted, that is, with single ticks.

说明:使用圆括号()的简单赋值将分号分隔的列表转换为一个数组,前提是在执行该操作时,您有正确的IFS。循环的标准像往常一样处理该数组中的各个项。请注意,在变量中给出的列表必须是“硬”引号,即单引号。

IFS must be saved and restored since Bash does not treat an assignment the same way as a command. An alternate workaround is to wrap the assignment inside a function and call that function with a modified IFS. In that case separate saving/restoring of IFS is not needed. Thanks for "Bize" for pointing that out.

由于Bash不像命令那样对待赋值,因此必须保存和恢复IFS。另一种变通方法是将赋值封装到函数中,并使用一个已修改的IFS调用该函数。在这种情况下,不需要单独保存或恢复IFS。感谢“Bize”指出这一点。

#28


0  

Maybe not the most elegant solution, but works with * and spaces:

也许不是最优雅的解决方案,但可以使用*和空格:

IN="bla@so me.com;*;john@home.com"
for i in `delims=${IN//[^;]}; seq 1 $((${#delims} + 1))`
do
   echo "> [`echo $IN | cut -d';' -f$i`]"
done

Outputs

输出

> [bla@so me.com]
> [*]
> [john@home.com]

Other example (delimiters at beginning and end):

其他例子(开始和结束的分隔符):

IN=";bla@so me.com;*;john@home.com;"
> []
> [bla@so me.com]
> [*]
> [john@home.com]
> []

Basically it removes every character other than ; making delims eg. ;;;. Then it does for loop from 1 to number-of-delimiters as counted by ${#delims}. The final step is to safely get the $ith part using cut.

基本上它除去了所有的字符;使delims如。;;;。然后,从1到数字分隔符循环,按${#delims}计算。最后一步是使用cut来安全地获得第i个部分。

#29


0  

Okay guys!

好,伙计们!

Here's my answer!

这是我的答案!

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

Why this approach is "the best" for me?

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

Because of two reasons:

因为两个原因:

  1. You do not need to escape the delimiter;
  2. 你不需要逃离分隔符;
  3. You will not have problem with blank spaces. The value will be properly separated in the array!
  4. 你不会对空格有问题。该值将在数组中正确地分离!

[]'s

[]的

#30


0  

you can apply awk to many situations

你可以在很多情况下使用awk。

echo "bla@some.com;john@home.com"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

also you can use this

你也可以用这个。

echo "bla@some.com;john@home.com"|awk -F';' '{print $1,$2}' OFS="\n"