外壳中是否有类似JavaScript的“ split()”之类的东西?


18

split()在JavaScript中使用它很容易将字符串分成数组。

那shell脚本呢?

说我想这样做:

$ script.sh var1_var2_var3

当用户将这样的字符串提供var1_var2_var3给script.sh时,它将在脚本内部将字符串转换为数组,例如

array=( var1 var2 var3 )
for name in ${array[@]}; do
    # some code
done

1
shell您在使用什么,bashIFS='_' read -a array <<< "${string}"
随便

perl也可以做到。它不是“纯”壳,但很常见。
Sobrique

@Sobrique我也不知道“ pure” shell的技术定义,但是有node.js。
emory

我倾向于研究“默认情况下它是否可能安装在我的linux机器上”,并且不要担心细节:)
Sobrique 2015年

Answers:


24

类似于Bourne / POSIX的shell具有split + glob运算符,每次在列表上下文中未引用参数扩展($var$-...),命令替换($(...))或算术扩展($((...)))时都会调用该操作符。

实际上,您在for name in ${array[@]}而不是时错误地调用了它for name in "${array[@]}"。(实际上,您应该注意,错误地调用该操作员是许多错误和安全漏洞的来源)。

该操作符配置有$IFS特殊参数(以告知要分割的字符(尽管要注意空格,制表符和换行符在那里有特殊处理))和-f禁用(set -f)或启用(set +fglob部分的选项。

还要注意,虽然Sin $IFS最初$IFS是用于分隔符的(在Bourne shell中),S但在POSIX shell中,in字符$IFS应视为分隔符终止符(请参见下面的示例)。

所以分裂_

string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
array=($string) # invoke the split+glob operator

for i in "${array[@]}"; do # loop over the array elements.

要查看分隔符分隔符之间的区别,请尝试:

string='var1_var2_'

这会将其拆分为var1var2(没有多余的空元素)。

因此,要使其类似于JavaScript的split(),您需要一个额外的步骤:

string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
temp=${string}_ # add an extra delimiter
array=($temp) # invoke the split+glob operator

(请注意,它将像JavaScript一样将一个空元素拆分$string1(不是0)元素split())。

要查看特殊处理标签,空格和换行符,请比较:

IFS=' '; string=' var1  var2  '

(您到达的位置var1var2)与

IFS='_'; string='_var1__var2__'

你在哪里得到:''var1''var2''

请注意,zsh除非在in shksh仿真中,shell不会像那样隐式调用该split + glob运算符。在那里,您必须显式调用它。$=string对于split部分,$~string对于glob部分($=~string对于两者),它还具有split运算符,您可以在其中指定分隔符:

array=(${(s:_:)string})

或保留空元素:

array=("${(@s:_:)string}")

请注意,这里s是用于split而不是定界的(也与$IFS已知的POSIX不符合zsh)。与JavaScript的不同之处split()在于,将空字符串拆分为0(而不是1)元素。

有一个显着区别$IFS-splitting的是,${(s:abc:)string}在分割abc字符串,同时用IFS=abc,将拆分上abc

使用zsh和时ksh93,可以通过在中加倍删除空格,制表符或换行符的特殊处理$IFS

作为历史记录,Bourne外壳(祖先外壳或现代POSIX外壳)始终会剥离空元素。它还有许多与$ @的拆分和扩展有关的错误,非默认值为$IFS。例如,IFS=_; set -f; set -- $@将不等同于IFS=_; set -f; set -- $1 $2 $3...

拆分正则表达式

现在,对于更接近JavaScript且split()可以在正则表达式上拆分的内容,您需要依赖外部实用程序。

在POSIX工具箱中,awk有一个split运算符,可以对扩展的正则表达式进行拆分(这些正则表达式或多或少是JavaScript支持的类似Perl的正则表达式的子集)。

split() {
  awk -v q="'" '
    function quote(s) {
      gsub(q, q "\\" q q, s)
      return q s q
    }
    BEGIN {
      n = split(ARGV[1], a, ARGV[2])
      for (i = 1; i <= n; i++) printf " %s", quote(a[i])
      exit
    }' "$@"
}
string=a__b_+c
eval "array=($(split "$string" '[_+]+'))"

zsh外壳具有内建的Perl兼容的正则表达式(其支持zsh/pcre模块),但用它来分割字符串,但可能是比较麻烦的。


是否需要对制表符,空格和换行符进行特殊处理?
cuonglm

1
@cuonglm,通常您希望在定界符为空格时对单词进行拆分$PATH:相反,对于非空白的定界符(例如,对进行拆分),您通常希望保留空元素。请注意,在Bourne Shell中,所有字符都受到特殊处理,ksh将其更改为仅对空白字符(尽管只有空格,制表符和换行符)进行了特殊处理。
斯特凡Chazelas

好吧,最近添加的Bourne Shell说明使我感到惊讶。为了完成,您是否应该添加注释以zsh处理字符串中包含2个或更多字符的字符串${(s:string:)var}?如果添加了,我可以删除答案:)
cuonglm

1
“还请注意,$ IFS中的S是用于分隔符,而不是分隔符”是什么意思?我了解机制,它忽略了尾随的分隔符,但S代表分隔符而不是定界符。至少,这就是我的bash手册所说的。
terdon

@terdon $IFS来自于Bourne外壳,在该外壳中是分离器,ksh更改了行为而未更改名称。我要强调一下split+glob(除了zsh或pdksh)不再简单地分裂了。
斯特凡Chazelas

7

是的,使用IFS并将其设置为_。然后用于read -a存储到数组中(-r关闭反斜杠扩展)。注意,这是专门针对bash的。ksh和zsh具有相似的功能,但语法略有不同,纯sh根本没有数组变量。

$ r="var1_var2_var3"
$ IFS='_' read -r -a array <<< "$r"
$ for name in "${array[@]}"; do echo "+ $name"; done
+ var1
+ var2
+ var3

来自man bash

-一个名字

单词被分配给数组变量aname的顺序索引,从0开始。aname在分配任何新值之前未设置。其他名称参数将被忽略。

IFS

内部字段分隔符,用于在扩展后进行单词拆分,并使用read Builtin命令将行拆分为单词。默认值为``''。

请注意read在第一条换行符处停止。通过-d ''read操作可以避免这种情况,但是在那种情况下,由于<<<操作员的原因,最后会有一个额外的换行符。您可以手动将其删除:

IFS='_' read -r -d '' -a array <<< "$r"
array[$((${#array[@]}-1))]=${array[$((${#array[@]}-1))]%?}

假定$r不包含换行符或反斜杠。另请注意,它仅在最新版本的bashShell中有效。
斯特凡Chazelas

@StéphaneChazelas好点。是的,这是字符串的“基本”情况。其余的,每个人都应该寻求您全面的答案。关于的版本bashread -a是在bash 4中引入的,对吧?
fedorqui's

1
不好意思,我想我<<<是最近才加入的,bash但似乎从2.05b(2002)开始就出现了。read -a比那还要大 <<<来自(zsh也受ksh93mksh和yash 支持),但read -a特定-A于bash(在ksh93,yash和zsh中)。
斯特凡Chazelas

@StéphaneChazelas是否有任何“简便”方法可以找到这些更改的发生时间?我说“容易”而不是深入研究发行文件,也许是显示所有文件的页面。
fedorqui 2015年

1
我查看更改日志。zsh还有一个git存储库,其历史可以追溯到3.1.5,并且其邮件列表也用于跟踪更改。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.