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

AGa*_*yer 19 shell string shell-script

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
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 25

类似 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.
Run Code Online (Sandbox Code Playgroud)

要查看separatordelimiter之间的区别,请尝试:

string='var1_var2_'
Run Code Online (Sandbox Code Playgroud)

这将把它分成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
Run Code Online (Sandbox Code Playgroud)

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

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

IFS=' '; string=' var1  var2  '
Run Code Online (Sandbox Code Playgroud)

(你得到的地方var1var2)与

IFS='_'; string='_var1__var2__'
Run Code Online (Sandbox Code Playgroud)

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

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

array=(${(s:_:)string})
Run Code Online (Sandbox Code Playgroud)

或保留空元素:

array=("${(@s:_:)string}")
Run Code Online (Sandbox Code Playgroud)

请注意,这里s用于拆分,而不是分隔(还有$IFS,已知的 POSIX 不一致性zsh)。它与 JavaScript 的不同之处split()在于,空字符串被拆分为 0 个(而不是 1 个)元素。

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

使用zshksh93,空格、制表符或换行符的特殊处理可以通过将它们在$IFS.

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

拆分正则表达式

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

在 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" '[_+]+'))"
Run Code Online (Sandbox Code Playgroud)

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


fed*_*qui 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
Run Code Online (Sandbox Code Playgroud)

来自man bash

-一个名字

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

国际金融中心

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

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

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