shell中有类似JavaScript的“split()”的东西吗?

shell中有类似JavaScript的“split()”的东西吗?

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

类似 Bourne/POSIX 的 shell 有一个 split+glob 运算符,每次在列表上下文中不加引号的参数扩展 ( $var, $-...)、命令替换 ( $(...)) 或算术扩展 ( ) 时都会调用它。$((...))

for name in ${array[@]}实际上,当您调用它而不是 时,您错误地调用了它for name in "${array[@]}"。 (实际上,你应该注意的是错误地调用该运算符是许多错误和安全漏洞的根源)。

该运算符配置有$IFS特殊参数(以告诉要分割的字符(但要注意空格、制表符和换行符在那里受到特殊处理))以及-f禁用 ( set -f) 或启用 ( set +f) 该glob部分的选项。

另请注意,虽然Sin$IFS最初(在 Bourne shell 中来自$IFS)用于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

(请注意,它将把一个空$string分成1(不是0) 元素,如 JavaScript 的split())。

要查看选项卡、空格和换行符的特殊处理,请比较:

IFS=' '; string=' var1  var2  '

(你得到的地方var1var2)与

IFS='_'; string='_var1__var2__'

在那里你得到:'', var1, '', var2, ''.

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

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

或保留空元素:

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

请注意,有s用于分裂, 不是界定(还有$IFS,已知的 POSIX 不符合项zsh)。它与 JavaScript 的不同之处split()在于,空字符串被分成 0 个(而不是 1 个)元素。

与 -splitting 的一个显着区别$IFS是在字符串${(s:abc:)string}上拆分abc,而使用IFS=abc, 则会在a,b或上拆分c

对于zshksh93,空格、制表符或换行符所受到的特殊处理可以通过将它们加倍来删除$IFS

作为一个历史记录,Bourne shell(祖先或现代 POSIX shell)总是剥离空元素。它还存在许多与 $@ 与非默认值 的拆分和扩展相关的错误$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" '[_+]+'))"

shellzsh内置了对 Perl 兼容正则表达式的支持(在其zsh/pcre模块中),但使用它来分割字符串虽然可能相对麻烦。

答案2

是的,使用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

-A一个名字

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

IFS

内部字段分隔符用于扩展后的字拆分以及使用 read 内置命令将行拆分为字。默认值为``''。

请注意,read停在第一个换行符处。传递-d ''toread以避免这种情况,但在这种情况下,由于运算符的原因,末尾会出现额外的换行符<<<。您可以手动删除它:

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

相关内容