将string拆分成Bash中的数组

在一个Bash脚本中,我想将一行分成几块,并把它们放入一个数组中。

该行:

Paris, France, Europe 

我想让他们像这样的数组:

 array[0] = Paris array[1] = France array[2] = Europe 

我想用简单的代码,命令的速度并不重要。 我该怎么做?

 IFS=', ' read -r -a array <<< "$string" 

要访问一个单独的元素:

 echo "${array[0]}" 

迭代元素:

 for element in "${array[@]}" do echo "$element" done 

要同时获得索引和值:

 for index in "${!array[@]}" do echo "$index ${array[index]}" done 

最后一个例子是有用的,因为Bash数组是稀疏的。 换句话说,你可以删除一个元素或添加一个元素,然后索引不连续。

 unset "array[1]" array[42]=Earth 

要获取数组中元素的数量:

 echo "${#array[@]}" 

如上所述,数组可以是稀疏的,所以你不应该使用长度来获得最后一个元素。 以下是您可以在Bash 4.2和更高版本中使用的方法:

 echo "${array[-1]}" 

在任何版本的Bash(从2.05b之后的某处):

 echo "${array[@]: -1:1}" 

较大的负偏移距离arrays末端较远。 请注意旧版本中减号前的空格。 这是必需的。

这是没有设置IFS的方法:

 string="1:2:3:4:5" set -f # avoid globbing (expansion of *). array=(${string//:/ }) for i in "${!array[@]}" do echo "$i=>${array[i]}" done 

这个想法是使用stringreplace:

 ${string//substring/replacement} 

用空格replace$ substring的所有匹配,然后使用replace的string来初始化一个数组:

 (element1 element2 ... elementN) 

注意:这个答案使用了split + glob操作符 。 因此,为了防止某些字符(如* )的扩展,暂停此脚本的globbing是一个好主意。

所有这个问题的答案都是这样或那样的错误。


错误的答案#1

 IFS=', ' read -r -a array <<< "$string" 

1:这是滥用$IFS$IFSvariables的值不是作为单个可变长度string分隔符,而是作为一 单字符string分隔符,其中从input行分割出的每个字段可以由任何字符终止在集合中(逗号空格,在这个例子中)。

实际上,对于那些真正的坚持者来说, $IFS的全部含义是稍微涉及的。 从bash手册 :

shell将IFS的每个字符视为分隔符,并将其他扩展的结果拆分为使用这些字符作为字段终止符的单词。 如果IFS未设置,或者其值正好是<space> <tab> <newline> ,则在之前扩展的结果开始和结束时的默认值,然后是<space><tab><newline>被忽略,并且不在开始或结束的任何IFS字符序列用于分隔单词。 如果IFS的值不是默认值,那么只要空白字符的值为空白字符<space><tab><newline>的序列在字的开始和结尾被忽略IFSIFS空格字符)。 IFS中不是IFS空格的任何字符,以及任何相邻的IFS空格字符,都会分隔一个字段。 IFS空格字符序列也被视为分隔符。 如果IFS的值为空,则不会发生分词。

基本上,对于$IFS非默认非空值,字段可以用(1)一个或多个字符的序列来分隔,这些字符都来自“IFS空白字符”集合(也就是<空格$IFS )中的任何位置都存在< ><tab><newline> (换行符),或者(2) $IFS中存在的任何非IFS空白字符以及任何“ IFS空白字符“将其包围在input行中。

对于OP来说,前一段描述的第二种分离模式可能是他想要的inputstring,但是我们可以相当确信我所描述的第一种分离模式是不正确的。 例如,如果他的inputstring是'Los Angeles, United States, North America'呢?

 IFS=', ' read -ra a <<<'Los Angeles, United States, North America'; declare -pa; ## declare -aa=([0]="Los" [1]="Angeles" [2]="United" [3]="States" [4]="North" [5]="America") 

2:即使您使用单字符分隔符(例如逗号本身,即没有以下空格或其他行李)的解决scheme,如果$stringvariables的值恰好包含任何LF,那么read一旦遇到第一个LF就会停止处理。 内置read每个调用只处理一行。 即使您只是将inputredirect到read语句,这也是正确的,就像我们在这个例子中用herestring机制所做的那样,因此未处理的input将被保证丢失。 内置read的代码不知道其包含的命令结构中的数据stream。

你可能会认为这不太可能引起问题,但是,如果可能的话,这是一个微妙的危险。 这是因为read实际上是两级input分裂:首先进入行,然后进入字段。 由于OP只需要一个级别的分割,所以内buildread这种用法是不合适的,我们应该避免它。

3:这个解决scheme的一个非显而易见的潜在问题是,如果它是空的, read总是删除尾部字段,否则保留空字段。 这是一个演示:

 string=', , a, , b, c, , , '; IFS=', ' read -ra a <<<"$string"; declare -pa; ## declare -aa=([0]="" [1]="" [2]="a" [3]="" [4]="b" [5]="c" [6]="" [7]="") 

也许OP不会在乎这个,但是它仍然是一个值得了解的限制。 这降低了解决scheme的稳健性和通用性。

这个问题可以通过在inputstring之前附加一个虚拟的尾随分隔符来解决,正如我稍后将要演示的。


错误的答案#2

 string="1:2:3:4:5" set -f # avoid globbing (expansion of *). array=(${string//:/ }) 

类似的想法:

 t="one,two,three" a=($(echo $t | tr ',' "\n")) 

(注:我在回答者似乎已经省略的命令replace周围添加了缺less的括号。)

类似的想法:

 string="1,2,3,4" array=(`echo $string | sed 's/,/\n/g'`) 

这些解决scheme利用数组分配中的字词拆分将string拆分为字段。 有趣的是,就像read一样,一般词拼接也使用$IFS特殊variables,虽然在这种情况下暗示它被设置为其默认值<space> <tab> <newline> ,因此任何一个序列或更多的IFS字符(现在全部是空白字符)被认为是字段分隔符。

这解决了由read两级分裂的问题,因为单词分裂本身只构成一个分裂级别。 但是和以前一样,这里的问题是inputstring中的单个字段可能已经包含$IFS字符,因此在字词拆分操作期间它们将被错误地分割。 对于这些回答者提供的任何样本inputstring(如何方便…),情况并非如此,但是这当然不会改变使用这个惯用语的任何代码库将会冒着风险的事实如果这个假设曾经在某一点上被侵犯过,那么它就会炸毁。 再次考虑我的'Los Angeles, United States, North America' (或'Los Angeles:United States:North America' )的反例。

此外,文字分割通常在文件名扩展之后 ( 又名path名扩展又称为 globbing),如果这样做,可能会破坏包含字符* ,?的单词? ,或[后跟] (如果extglob已设置, extglob ?*+@! extglob括号碎片)通过将其匹配到文件系统对象并相应地扩展单词(“globs”)来实现。 这三个回答者中的第一个通过事先运行set -f来禁用globbing,从而巧妙地削弱了这个问题。 从技术上讲,这是有效的(尽pipe之后你可能需要添加set +f来为可能依赖的后续代码重新启用globbing),但是为了破解基本的string到数组的parsing操作,不得不弄乱全局shell设置在本地代码。

这个答案的另一个问题是,所有的空白字段将会丢失。 这可能是也可能不是问题,取决于应用程序。

注意:如果你打算使用这个解决scheme,最好使用参数扩展的${string//:/ } “模式replace”forms,而不是去调用一个命令replace),启动一个pipe道,并运行一个外部可执行文件( trsed ),因为参数扩展纯粹是一个shell内部操作。 (而且,对于trsed解决scheme,inputvariables在命令replace中应该用双引号括起来;否则,分词将在echo命令中生效,并且可能与字段值混淆,而$(...)命令replace的forms比旧的`...`格式更可取,因为它简化了命令replace的嵌套,并允许文本编辑器更好的语法高亮显示。


错误的答案#3

 str="a, b, c, d" # assuming there is a space after ',' as in Q arr=(${str//,/}) # delete all occurrences of ',' 

这个答案几乎和#2一样 。 不同之处在于回答者已经假设这些字段是由两个字符分隔的,其中一个字符在默认$IFS表示,另一个字符不是。 他已经通过使用模式replace扩展去除非IFS表示的字符并且然后使用分词来分割存活的由IFS表示的分隔符字段上的字段来解决这个相当具体的情况。

这不是一个非常通用的解决scheme。 此外,可以认为,逗号确实是这里的“主要”分界符,剥离它然后根据空间分裂的特点是完全错误的。 再次考虑我的反例: 'Los Angeles, United States, North America'

此外,文件名扩展可能会破坏扩展的单词,但是可以通过使用set -f临时禁用分配的globbing,然后set +f来防止这种情况。

另外,所有的空白字段都将丢失,这取决于应用程序,这可能是也可能不是问题。


错误的答案#4

 string='first line second line third line' oldIFS="$IFS" IFS=' ' IFS=${IFS:0:1} # this is useful to format your code with tabs lines=( $string ) IFS="$oldIFS" 

这与#2#3类似,它使用分词来完成工作,现在代码明确地设置$IFS只包含inputstring中存在的单字符字段分隔符。 应该重复的是,这不能用于多字符字段分隔符,如OP的逗号分隔符。 但是对于这个例子中使用的LF这样的单字符分隔符来说,它实际上已经接近完美了。 正如我们以前看到的错误答案一样,这些字段不能无意中分裂,只有一个级别的分裂。

一个问题是,文件名扩展会损坏受影响的单词,如前所述,但是可以通过将set -fset +f的关键语句包装来解决。

另一个潜在的问题是,由于LF被定义为前面定义的“IFS空白字符”,因此所有空字段都将丢失,就像在#2#3中一样 。 如果分隔符碰巧是一个非IFS空格字符,这当然不会成为问题,并且依赖于应用程序,它可能无关紧要,但是这确实破坏了解决scheme的一般性。

所以,总结一下,假设你有一个单字符的分隔符,它既可以是非IFS空格字符,也可以不关心空字段,而且可以把set -fset +f ,那么这个解决scheme起作用,否则不行。

(另外,为了信息的缘故,用$'...'语法可以更容易的将b赋值给bash中的一个variables,例如IFS=$'\n';


错误的答案#5

 countries='Paris, France, Europe' OIFS="$IFS" IFS=', ' array=($countries) IFS="$OIFS" 

类似的想法:

 IFS=', ' eval 'array=($string)' 

这个解决scheme实际上是#1 (它将$IFS设置$IFS逗号空间)和#2-4 (因为它使用单词拆分将string拆分为字段)之间的交叉。 正因为如此,它遭受了大多数困扰上述错误答案的问题,有点像最糟糕的世界。

此外,关于第二个变体,它可能看起来像eval调用是完全不必要的,因为它的参数是一个单引号string文字,因此是静态的已知。 但是以这种方式使用eval其实有一个非常明显的好处。 通常情况下,当你运行一个简单的命令, 它只包含一个variables赋值,意思是后面没有实际的命令字时,赋值在shell环境中生效:

 IFS=', '; ## changes $IFS in the shell environment 

即使简单的命令涉及多个variables赋值也是如此; 再次,只要没有命令字,所有variables赋值都会影响shell环境:

 IFS=', ' array=($countries); ## changes both $IFS and $array in the shell environment 

但是,如果variables赋值附加到命令名称(我喜欢称之为“前缀赋值”),那么它不会影响shell环境,而只会影响执行的命令的环境,而不pipe它是否为内置或外部:

 IFS=', ' :; ## : is a builtin command, the $IFS assignment does not outlive it IFS=', ' env; ## env is an external command, the $IFS assignment does not outlive it 

bash手册中的相关引用:

如果没有命令名称,则variables赋值会影响当前的shell环境。 否则,variables将被添加到执行的命令的环境中,而不会影响当前的shell环境。

可以利用variables赋值的这个特性来暂时的改变$IFS ,这样就可以避免像第一个variables中的$OIFSvariables那样的整个保存和恢复游戏。 但是我们这里面临的挑战是我们需要运行的命令本身就是一个单纯的variables赋值,因此它不会包含命令字来使$IFS赋值临时。 你可能会认为自己,为什么不把一个没有操作的命令字添加到像: builtin这样的语句中来使$IFS赋值临时? 这是行不通的,因为它会使$array赋值临时为止:

 IFS=', ' array=($countries) :; ## fails; new $array value never escapes the : command 

所以,我们实际上处于僵局,有点捉襟见肘。 但是,当eval运行它的代码时,它会在shell环境中运行它,就好像它是普通的静态源代码一样,因此我们可以在eval参数内部运行$array赋值,使其在shell环境中生效。以eval命令为前缀的$IFS前缀分配不会超出eval命令。 这正是在这个解决scheme的第二个变体中使用的技巧:

 IFS=', ' eval 'array=($string)'; ## $IFS does not outlive the eval command, but $array does 

所以,正如你所看到的那样,这实际上是一个非常聪明的把戏,并且以一种非常不明显的方式完成了所需要的事情(至less在赋值效果方面)。 我实际上并不反对这个伎俩,尽pipe有eval的参与; 只是要小心单引号参数string,以防范安全威胁。

但是,再次,由于“世界上最糟糕的”问题的集聚,这对OP的要求仍然是一个错误的答案。


错误的答案#6

 IFS=', '; array=(Paris, France, Europe) IFS=' ';declare -a array=(Paris France Europe) 

呃…什么? OP有一个stringvariables需要被parsing成一个数组。 这个“答案”以粘贴到数组文字中的inputstring的逐字内容开始。 我想这是一个办法。

它看起来像回答者可能已经假定$IFSvariables影响所有上下文中的所有bashparsing,这是不正确的。 从bash手册:

IFS内部字段分隔符,用于扩展后的字词拆分,以及使用读取内置命令将字词拆分为单词。 默认值是<space> <tab> <newline>

所以$IFS特殊variables实际上只在两个上下文中使用:(1) 扩展后执行的分词(意味着不在parsingbash源代码时);(2)通过内置read将input行分割成单词。

让我试着更清楚一点。 我认为在parsing执行之间做一个区分也许是件好事。 Bash必须首先parsing源代码,这显然是一个parsing事件,然后它执行代码,这是扩展到图片的时候。 扩张实际上是一个执行事件。 此外,我对上面刚刚引用的$IFSvariables的描述有所$IFS 。 而不是说在扩展之后执行分词,我会说扩展期间执行分词,或者甚至更准确地说,分词是扩展过程的一部分 。 “分词”这个词只是指这个扩展的步骤; 它不应该被用来指代bash源代码的parsing,尽pipe不幸的是这些文档看起来好像在“拆分”和“单词”这样的词语中出现了很多。 下面是bash手册的linux.die.net版本的相关摘录:

将命令行分割成文字后,再进行扩展。 有七种扩展: 括号扩展波形扩展参数和variables扩展命令replace算术扩展 ,分path名扩展

扩展的顺序是:支撑扩展; 代字符扩展,参数和variables扩展,算术扩展和命令replace(按照从左到右的方式完成); 分词; 和path名称扩展。

你可能会认为手册的GNU版本稍微好一点,因为它在扩展部分的第一句中select了“令牌”而不是“单词”这个词:

将命令行分割为令牌后,再进行扩展。

重要的一点是, $IFS不会改变bashparsing源代码的方式。 bash源代码的parsing实际上是一个非常复杂的过程,涉及识别shell语法的各种元素,如命令序列,命令列表,pipe道,参数扩展,算术replace和命令replace。 大多数情况下,bashparsing过程不能被用户级动作(如variables赋值)所改变(实际上,这个规则有一些小的例外;例如,请参阅各种compatxx shell设置 ,这些设置可以改变parsing行为的某些方面在即时)。 然后根据在上述文档摘录中分解的“扩展”的一般过程来扩展由这个复杂parsing过程产生的上游“词”/“标记”,其中将扩展(扩展的?)文本分词为下游单词只是这个过程的一个步骤。 单词分裂只触及已经从前面的扩展步骤吐出的文本; 它不会影响从源字节stream中parsing出的文本文本。


错误的答案#7

 string='first line second line third line' while read -r line; do lines+=("$line"); done <<<"$string" 

这是最好的解决scheme之一。 请注意,我们正在使用read 。 我刚才不是说过, read是不恰当的,因为它执行两个级别的分裂,当我们只需要一个? 这里的诀窍是你可以调用read来实现一个分割级别,特别是每个调用只分割一个字段,这就需要花费不得不在循环中重复调用它。 这是一个有趣的手段,但它的作品。

但是有问题。 首先:当您提供至less一个NAME参数时,它将自动忽略从inputstring中分离的每个字段中的前导空格和尾部空格。 无论是否将$IFS设置为默认值,都会发生这种情况,如本文前面所述。 现在,OP可能不关心他的具体用例,事实上,这可能是parsing行为的一个理想特征。 但不是每个想要将stringparsing成字段的人都会想要这样做。 有一个解决scheme,但是: read有点不明显的用法是传递零个NAME参数。 在这种情况下, read将把从inputstream获得的整个input行存储在一个名为$REPLY的variables中,作为奖励,它不会从该值中删除前导和尾随空白。 这是一个非常强大的使用read ,我已经在我的shell编程事业中经常使用。 以下是行为差异的演示:

 string=$' ab \ncd \nef '; ## input string a=(); while read -r line; do a+=("$line"); done <<<"$string"; declare -pa; ## declare -aa=([0]="ab" [1]="cd" [2]="ef") ## read trimmed surrounding whitespace a=(); while read -r; do a+=("$REPLY"); done <<<"$string"; declare -pa; ## declare -aa=([0]=" ab " [1]=" cd " [2]=" ef ") ## no trimming 

这个解决scheme的第二个问题是,它实际上并没有解决自定义字段分隔符的问题,比如OP的逗号空间。 和以前一样,不支持多字符分隔符,这是这个解决scheme的一个不幸的限制。 我们可以尝试通过指定-d选项的分隔符来至less分割逗号,但看看会发生什么:

 string='Paris, France, Europe'; a=(); while read -rd,; do a+=("$REPLY"); done <<<"$string"; declare -pa; ## declare -aa=([0]="Paris" [1]=" France") 

可以预料的是,未被logging的周围空白被拉到了字段值中,因此这将不得不通过修剪操作来纠正(这也可以直接在while循环中完成)。 但还有一个明显的错误:欧洲失踪了! 发生了什么事? 答案是,如果read结束文件(在这种情况下,我们可以称之为string结尾), read返回一个失败的返回码,而不会在最终字段中遇到最终的字段终止符。 这会导致while循环提前中断,我们会丢失最后一个字段。

从技术上讲,同样的错误也影响了前面的例子。 不同之处在于字段分隔符被认为是LF,当你不指定-d选项时,这是缺省的,而<<< (“here-string”)机制会自动将一个LF附加到string中然后将其作为input提供给命令。 因此,在这些情况下,我们偶然地通过无意中在input端添加了一个附加的虚拟终结符来意外地解决了最终字段丢失的问题。 我们称这个解决scheme为“虚拟终结者”解决scheme。 我们可以手动应用虚拟终结者解决scheme,通过在自己的string中实例化时将其与自己的inputstring连接起来,来自定义分隔符:

 a=(); while read -rd,; do a+=("$REPLY"); done <<<"$string,"; declare -pa; declare -aa=([0]="Paris" [1]=" France" [2]=" Europe") 

在那里,问题解决了。 另一个解决scheme是,只有在(1) read返回的失败和(2) $REPLY为空的情况下才打破while循环,这意味着read在读取文件结束之前不能读取任何字符。 演示:

 a=(); while read -rd,|| [[ -n "$REPLY" ]]; do a+=("$REPLY"); done <<<"$string"; declare -pa; ## declare -aa=([0]="Paris" [1]=" France" [2]=$' Europe\n') 

这种方法还揭示了秘密的LF,它通过<<<redirect操作符自动附加到here-string。 当然可以通过前面介绍的显式修剪操作单独剥离,但显然手动伪终结器方法直接解决了这个问题,所以我们可以继续这样做。 手工虚拟终结器解决scheme实际上是非常方便的,因为它一次性解决了这两个问题(丢失最终字段问题和附加LF问题)。

所以,总的来说,这是一个相当强大的解决scheme。 剩下的弱点就是缺乏对多字符分隔符的支持,稍后我会解决。


错误的答案#8

 string='first line second line third line' readarray -t lines <<<"$string" 

(这实际上来自#7的同一个职位;回答者在同一篇文章中提供了两个解决scheme。)

readarray builtin是mapfile的同义词,是理想的。 这是一个内置的命令,它将一个字节streamparsing为一个数组variables, 不会弄乱循环,条件,replace或其他任何东西。 它不会偷偷地从inputstring中删除任何空格。 而且(如果没有给出-O ),在分配给它之前,它会方便地清除目标数组。 但是这还不是完美的,所以我的批评是“错误的答案”。

首先,为了解决这个问题,请注意,就像执行字段parsing时的read行为一样, readarray尾部字段(如果为空)删除。 同样,这可能不是OP的问题,但可能是一些使用情况。 我会马上回来。

其次,和以前一样,它不支持多字符分隔符。 我也会马上给你解决这个问题。

第三,写的解决scheme不parsingOP的inputstring,事实上,它不能被用来parsing它。 我也会马上展开。

由于上述原因,我仍然认为这是对OP问题的“错误答案”。 下面我会给我认为是正确的答案。


正确答案

这是一个天真的尝试,通过指定-d选项来使#8工作:

 string='Paris, France, Europe'; readarray -td, a <<<"$string"; declare -pa; ## declare -aa=([0]="Paris" [1]=" France" [2]=$' Europe\n') 

我们看到结果与我们从#7中讨论的循环read解决scheme的双重条件方法得到的结果相同。 我们几乎可以用手工虚拟终结技巧解决这个问题:

 readarray -td, a <<<"$string,"; declare -pa; ## declare -aa=([0]="Paris" [1]=" France" [2]=" Europe" [3]=$'\n') 

这里的问题是, readarray保留了尾部字段,因为<<<redirect操作符将LF附加到inputstring,因此尾部字段不是空的(否则它将被删除)。 我们可以通过事后明确地取消设置最后的数组元素来解决这个问题:

 readarray -td, a <<<"$string,"; unset 'a[-1]'; declare -pa; ## declare -aa=([0]="Paris" [1]=" France" [2]=" Europe") 

唯一存在的两个问题实际上是相关的:(1)需要修剪的外部空白;(2)缺less对多字符分隔符的支持。

之后可以修剪空白(例如,请参阅如何从Bashvariables修剪空格? )。 但是,如果我们能够破解多字符分隔符,那么一次就能解决这两个问题。

不幸的是,没有直接的方法来获取多字符分隔符的工作。 我想过的最好的解决scheme是预处理inputstring,用单字符分隔符replace多字符分隔符,这将保证不会与inputstring的内容相冲突。 有这个保证的唯一字符是NUL字节 。 这是因为,在bash中(尽pipe不在zsh中),variables不能包含NUL字节。 这个预处理步骤可以在进程replace中内联完成。 以下是使用awk的方法 :

 readarray -td '' a < <(awk '{ gsub(/, /,"\0"); print; }' <<<"$string, "); unset 'a[-1]'; declare -pa; ## declare -aa=([0]="Paris" [1]="France" [2]="Europe") 

那里,终于! 这种解决scheme不会在中间错误地分割字段,不会过早地截断,不会丢失空字段,在文件名扩展时不会自行破坏,不会自动剥离前后的空白,最终不会留下偷渡的LF,不需要循环,并不能解决单个字符的分隔符。


修剪解决scheme

最后,我想使用readarray -C callback选项来演示自己相当复杂的修剪解决scheme。 不幸的是,我已经没有足够的空间来对付Stack Overflow的30,000个字符的限制,所以我不能解释它。 我将把它作为读者的练习。

 function mfcb { local val="$4"; "$1"; eval "$2[$3]=\$val;"; }; function val_ltrim { if [[ "$val" =~ ^[[:space:]]+ ]]; then val="${val:${#BASH_REMATCH[0]}}"; fi; }; function val_rtrim { if [[ "$val" =~ [[:space:]]+$ ]]; then val="${val:0:${#val}-${#BASH_REMATCH[0]}}"; fi; }; function val_trim { val_ltrim; val_rtrim; }; readarray -c1 -C 'mfcb val_trim a' -td, <<<"$string,"; unset 'a[-1]'; declare -pa; ## declare -aa=([0]="Paris" [1]="France" [2]="Europe") 

有时我发现接受的答案中描述的方法不起作用,特别是如果分隔符是回车。
在这些情况下,我用这种方式解决

 string='first line second line third line' oldIFS="$IFS" IFS=' ' IFS=${IFS:0:1} # this is useful to format your code with tabs lines=( $string ) IFS="$oldIFS" for line in "${lines[@]}" do echo "--> $line" done 

接受的答案适用于一行中的值。
如果variables有几行:

 string='first line second line third line' 

我们需要一个非常不同的命令来获取所有行:

while read -r line; do lines+=("$line"); done <<<"$string"

或者更简单的bash readarray

 readarray -t lines <<<"$string" 

利用printffunction打印所有行非常容易:

 printf ">[%s]\n" "${lines[@]}" >[first line] >[ second line] >[ third line] 
 t="one,two,three" a=($(echo "$t" | tr ',' '\n')) echo "${a[2]}" 

打印三个

This is similar to the approach by Jmoney38, but using sed:

 string="1,2,3,4" array=(`echo $string | sed 's/,/\n/g'`) echo ${array[0]} 

Prints 1

尝试这个

 IFS=', '; array=(Paris, France, Europe) for item in ${array[@]}; do echo $item; done 

这很简单。 If you want, you can also add a declare (and also remove the commas):

 IFS=' ';declare -a array=(Paris France Europe) 

The IFS is added to undo the above but it works without it in a fresh bash instance

用这个:

 countries='Paris, France, Europe' OIFS="$IFS" IFS=', ' array=($countries) IFS="$OIFS" #${array[1]} == Paris #${array[2]} == France #${array[3]} == Europe 

Another approach can be:

 str="a, b, c, d" # assuming there is a space after ',' as in Q arr=(${str//,/}) # delete all occurrences of ',' 

After this 'arr' is an array with four strings. This doesn't require dealing IFS or read or any other special stuff hence much simpler and direct.

UPDATE: Don't do this, due to problems with eval.

With slightly less ceremony:

 IFS=', ' eval 'array=($string)' 

例如

 string="foo, bar,baz" IFS=', ' eval 'array=($string)' echo ${array[1]} # -> bar 

另一种方法是:

 string="Paris, France, Europe" IFS=', ' arr=(${string}) 

Now your elements are stored in "arr" array. To iterate through the elements:

 for i in ${arr[@]}; do echo $i; done