如何在Bash中对数组进行排序

时间:2011-09-16 09:06:58

标签: arrays bash shell sorting

我在Bash中有一个数组,例如:

array=(a c b f 3 5)

我需要对数组进行排序。不只是以排序的方式显示内容,而是获取带有已排序元素的新数组。新排序的数组可以是全新的也可以是旧的。

17 个答案:

答案 0 :(得分:167)

你真的不需要那么多代码:

IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS

支持元素中的空格(只要它不是换行符),在Bash 3.x中工作。

e.g:

$ array=("a c" b f "3 5")
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
$ printf "[%s]\n" "${sorted[@]}"
[3 5]
[a c]
[b]
[f]

注意: @sorontar pointed out如果元素包含*?等通配符,则需要小心:

  

sorted =($(...))部分使用“split and glob”运算符。你应该关闭glob:set -fset -o noglobshopt -op noglob,或者像*这样的数组元素将扩展为文件列表。

发生了什么:

结果是按顺序发生的六件事:

  1. IFS=$'\n'
  2. "${array[*]}"
  3. <<<
  4. sort
  5. sorted=($(...))
  6. unset IFS
  7. 首先,IFS=$'\n'

    这是我们操作的一个重要部分,它以下列方式影响2和5的结果:

    鉴于:

    • "${array[*]}"扩展为由IFS
    • 的第一个字符分隔的每个元素
    • sorted=()通过拆分IFS
    • 的每个字符来创建元素

    IFS=$'\n' sets things up以便使用新行作为分隔符扩展元素,然后以每行成为元素的方式创建元素。 (即拆分新线。)

    通过新行划分非常重要,因为这是sort的操作方式(每行排序)。仅按 拆分新行并不重要,但需要保留包含空格或制表符的元素。

    IFS的默认值是空格标签,后跟新行,并且不适合为了我们的运作。

    接下来,sort <<<"${array[*]}"部分

    名为here strings

    <<<采用"${array[*]}"的扩展,如上所述,并将其提供给sort的标准输入。

    在我们的示例中,sort被提供以下字符串:

    a c
    b
    f
    3 5
    

    由于sort 排序,它会产生:

    3 5
    a c
    b
    f
    

    接下来,sorted=($(...))部分

    名为command substitution$(...)部分使其内容(sort <<<"${array[*]})作为正常命令运行,同时将生成的标准输出作为<{1}}所在的文字。

    在我们的例子中,这产生类似于简单写作的东西:

    $(...)
    然后

    sorted=(3 5 a c b f ) 成为通过在每个新行上拆分此文字而创建的数组。

    最后,sorted

    这会将unset IFS的值重置为默认值,这只是一种很好的做法。

    这是为了确保我们不会在以后的脚本中依赖IFS的任何事情造成麻烦。 (否则我们需要记住我们已经改变了一些东西 - 对于复杂的脚本来说可能是不切实际的。)

答案 1 :(得分:33)

原始回复:

array=(a c b "f f" 3 5)
readarray -t sorted < <(for a in "${array[@]}"; do echo "$a"; done | sort)

输出:

$ for a in "${sorted[@]}"; do echo "$a"; done
3
5
a
b
c
f f

注意此版本可以处理包含特殊字符或空格的值(除了换行符)

注意 bash 4+支持readarray。


修改根据@Dimitre的建议,我已将其更新为:

readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1)

这有利于甚至理解排序元素,并正确嵌入换行符。不幸的是,正如@ruakh正确发出信号,这并不意味着readarray的结果正确,因为readarray无法使用NUL代替常规换行符作为行分隔符。

答案 2 :(得分:32)

这是一个纯粹的Bash快速排序实现:

#!/bin/bash

# quicksorts positional arguments
# return is in array qsort_ret
qsort() {
   local pivot i smaller=() larger=()
   qsort_ret=()
   (($#==0)) && return 0
   pivot=$1
   shift
   for i; do
      if [[ $i < $pivot ]]; then
         smaller+=( "$i" )
      else
         larger+=( "$i" )
      fi
   done
   qsort "${smaller[@]}"
   smaller=( "${qsort_ret[@]}" )
   qsort "${larger[@]}"
   larger=( "${qsort_ret[@]}" )
   qsort_ret=( "${smaller[@]}" "$pivot" "${larger[@]}" )
}

用作例如

$ array=(a c b f 3 5)
$ qsort "${array[@]}"
$ declare -p qsort_ret
declare -a qsort_ret='([0]="3" [1]="5" [2]="a" [3]="b" [4]="c" [5]="f")'

这个实现是递归的...所以这里是一个迭代的快速排序:

#!/bin/bash

# quicksorts positional arguments
# return is in array qsort_ret
# Note: iterative, NOT recursive! :)
qsort() {
   (($#==0)) && return 0
   local stack=( 0 $(($#-1)) ) beg end i pivot smaller larger
   qsort_ret=("$@")
   while ((${#stack[@]})); do
      beg=${stack[0]}
      end=${stack[1]}
      stack=( "${stack[@]:2}" )
      smaller=() larger=()
      pivot=${qsort_ret[beg]}
      for ((i=beg+1;i<=end;++i)); do
         if [[ "${qsort_ret[i]}" < "$pivot" ]]; then
            smaller+=( "${qsort_ret[i]}" )
         else
            larger+=( "${qsort_ret[i]}" )
         fi
      done
      qsort_ret=( "${qsort_ret[@]:0:beg}" "${smaller[@]}" "$pivot" "${larger[@]}" "${qsort_ret[@]:end+1}" )
      if ((${#smaller[@]}>=2)); then stack+=( "$beg" "$((beg+${#smaller[@]}-1))" ); fi
      if ((${#larger[@]}>=2)); then stack+=( "$((end-${#larger[@]}+1))" "$end" ); fi
   done
}

在这两种情况下,您都可以更改您使用的顺序:我使用字符串比较,但您可以使用算术比较,比较wrt文件修改时间等,只需使用相应的测试;你甚至可以使它更通用,并使用第一个参数,即测试函数使用,例如,

#!/bin/bash

# quicksorts positional arguments
# return is in array qsort_ret
# Note: iterative, NOT recursive! :)
# First argument is a function name that takes two arguments and compares them
qsort() {
   (($#<=1)) && return 0
   local compare_fun=$1
   shift
   local stack=( 0 $(($#-1)) ) beg end i pivot smaller larger
   qsort_ret=("$@")
   while ((${#stack[@]})); do
      beg=${stack[0]}
      end=${stack[1]}
      stack=( "${stack[@]:2}" )
      smaller=() larger=()
      pivot=${qsort_ret[beg]}
      for ((i=beg+1;i<=end;++i)); do
         if "$compare_fun" "${qsort_ret[i]}" "$pivot"; then
            smaller+=( "${qsort_ret[i]}" )
         else
            larger+=( "${qsort_ret[i]}" )
         fi
      done
      qsort_ret=( "${qsort_ret[@]:0:beg}" "${smaller[@]}" "$pivot" "${larger[@]}" "${qsort_ret[@]:end+1}" )
      if ((${#smaller[@]}>=2)); then stack+=( "$beg" "$((beg+${#smaller[@]}-1))" ); fi
      if ((${#larger[@]}>=2)); then stack+=( "$((end-${#larger[@]}+1))" "$end" ); fi
   done
}

然后你可以拥有这个比较功能:

compare_mtime() { [[ $1 -nt $2 ]]; }

并使用:

$ qsort compare_mtime *
$ declare -p qsort_ret

让当前文件夹中的文件按修改时间排序(最新的第一个)。

请注意。这些功能都是纯粹的Bash!没有外部工具,也没有子壳!它们是安全的,你可能有任何有趣的符号(空格,换行符,圆形字符等)。

答案 3 :(得分:26)

如果您不需要处理数组元素中的特殊shell字符:

array=(a c b f 3 5)
sorted=($(printf '%s\n' "${array[@]}"|sort))

使用 bash ,无论如何你都需要一个外部排序程序。

使用 zsh 不需要外部程序,并且可以轻松处理特殊的shell字符:

% array=('a a' c b f 3 5); printf '%s\n' "${(o)array[@]}" 
3
5
a a
b
c
f

ksh set -s来排序 ASCIIbetically

答案 4 :(得分:8)

在从慕尼黑到法兰克福的3小时火车旅行中(我明天开始因为慕尼黑啤酒节而无法到达),我正在思考我的第一篇文章。对于一般排序函数,使用全局数组是一个更好的想法。以下函数处理任意字符串(换行符,空格等):

declare BSORT=()
function bubble_sort()
{   #
    # @param [ARGUMENTS]...
    #
    # Sort all positional arguments and store them in global array BSORT.
    # Without arguments sort this array. Return the number of iterations made.
    #
    # Bubble sorting lets the heaviest element sink to the bottom.
    #
    (($# > 0)) && BSORT=("$@")
    local j=0 ubound=$((${#BSORT[*]} - 1))
    while ((ubound > 0))
    do
        local i=0
        while ((i < ubound))
        do
            if [ "${BSORT[$i]}" \> "${BSORT[$((i + 1))]}" ]
            then
                local t="${BSORT[$i]}"
                BSORT[$i]="${BSORT[$((i + 1))]}"
                BSORT[$((i + 1))]="$t"
            fi
            ((++i))
        done
        ((++j))
        ((--ubound))
    done
    echo $j
}

bubble_sort a c b 'z y' 3 5
echo ${BSORT[@]}

打印:

3 5 a b c z y

创建相同的输出
BSORT=(a c b 'z y' 3 5) 
bubble_sort
echo ${BSORT[@]}

请注意,Bash内部可能使用智能指针,因此交换操作可能便宜(尽管我对此表示怀疑)。但是,bubble_sort表明更高级的函数(如merge_sort)也在shell语言的范围内。

答案 5 :(得分:6)

tl; dr

排序数组a_in并将结果存储在a_out中(元素不得包含嵌入换行符 [1] ):

Bash v4 +:

readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)

Bash v3:

IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)

优于antak's solution的优势:

  • 您无需担心意外的通配(将数组元素意外解释为文件名模式),因此无需额外命令即可禁用通配(set -fset +f以恢复它稍后)。

  • 您无需担心使用IFS重置unset IFS [2]

可选阅读:说明和示例代码

以上将Bash代码与外部实用程序 sort 结合使用,以获得适用于任意 -line元素的解决方案词汇或数字排序(可选择按字段)

  • 效果:对于大约20个元素或更多元素,这比纯粹的Bash解决方案 更快 - 一旦超过100个元素,就会越来越强烈 (确切的阈值取决于您的具体输入,机器和平台。)

    • 它快速的原因是避免Bash循环
  • printf '%s\n' "${a_in[@]}" | sort 执行排序(词汇上,默认情况下 - 请参阅sort's POSIX spec):

    • "${a_in[@]}"安全地扩展为数组a_in的元素,作为单个参数,无论它们包含什么(包括空格)。

    • printf '%s\n'然后按原样打印每个参数 - 即每个数组元素 - 在它自己的行上。

  • 请注意使用process substitution (<(...)) 将排序后的输出作为read / readarray的输入提供(通过重定向到stdin,{{ 1}}),因为 < / read必须在当前 shell中运行(不得在子shell中运行)以使输出变量readarray对当前shell可见(使变量保持在脚本的其余部分中定义)。

  • a_out的输出读入数组变量

    • Bash v4 +:sortreadarray -t a_out输出的各行读取到数组变量sort的元素中,而不在每个元素中包含尾随a_out\n)。

    • Bash v3:-t不存在,因此必须使用readarray
      read告诉IFS=$'\n' read -d '' -r -a a_out读取数组(read)变量-a,读取整个输入,跨行(a_out),但将其拆分为数组元素新行(-d ''IFS=$'\n'生成一个文字换行符(LF),即所谓的ANSI C-quoted string)。 ($'\n',一个几乎总是与-r一起使用的选项,会禁用对read字符的意外处理。)

带注释的示例代码:

\

由于使用#!/usr/bin/env bash # Define input array `a_in`: # Note the element with embedded whitespace ('a c')and the element that looks like # a glob ('*'), chosen to demonstrate that elements with line-internal whitespace # and glob-like contents are correctly preserved. a_in=( 'a c' b f 5 '*' 10 ) # Sort and store output in array `a_out` # Saving back into `a_in` is also an option. IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort) # Bash 4.x: use the simpler `readarray -t`: # readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort) # Print sorted output array, line by line: printf '%s\n' "${a_out[@]}" 没有选项,这会产生词汇排序(字母前的数字排序,数字序列的词汇处理,而不是数字):

sort

如果您希望按第一个字段进行数字排序,则可以使用* 10 5 a c b f 而不仅仅是sort -k1,1n,这会产生(数字之前的非数字排序和数字)正确排序):

sort

[1]要处理带有嵌入换行符的元素,请使用以下变体(Bash v4 +, GNU * a c b f 5 10 ):
sort
Michał Górny's helpful answer有一个Bash v3解决方案。

[2]在Bash v3变体中设置readarray -d '' -t a_out < <(printf '%s\0' "${a_in[@]}" | sort -z) 时,更改作用于命令
相比之下,antak的答案中IFS后面的内容是赋值而不是命令,在这种情况下,IFS=$'\n' 更改是全局

答案 6 :(得分:5)

另一种使用外部sort并处理任何特殊字符的解决方案(NULs除外:))。应该使用bash-3.2和GNU或BSD sort(遗憾的是,POSIX不包括-z)。

local e new_array=()
while IFS= read -r -d '' e; do
    new_array+=( "${e}" )
done < <(printf "%s\0" "${array[@]}" | LC_ALL=C sort -z)

首先看一下最后的输入重定向。我们使用printf内置来写出数组元素,零终止。引用确保数组元素按原样传递,并且shell printf的细节使其为每个剩余参数重用格式字符串的最后部分。也就是说,它等同于:

for e in "${array[@]}"; do
    printf "%s\0" "${e}"
done

然后将以null结尾的元素列表传递给sort-z选项使它读取以null结尾的元素,对它们进行排序并输出以null结尾的元素。如果您只需要获取唯一元素,则可以传递-u,因为它比uniq -z更便携。 LC_ALL=C确保独立于区域设置的稳定排序顺序 - 有时对脚本有用。如果您希望sort尊重语言环境,请将其删除。

<()构造获取要从生成的管道读取的描述符,<while循环的标准输入重定向到它。如果您需要访问管道内的标准输入,您可以使用另一个描述符 - 为读者练习:)。

现在,回到开头。 read内置读取重定向标准输出的输出。设置空IFS会禁用在此处不必要的单词拆分 - 因此,read会读取单个提供的变量的输入的整个“行”。 -r选项也禁用此处不需要的转义处理。最后,-d ''将行分隔符设置为NUL - 也就是说,告诉read读取以零结尾的字符串。

因此,对于每个连续的以零端接的数组元素,循环执行一次,其值存储在e中。该示例只是将项目放在另一个数组中,但您可能更喜欢直接处理它们:)。

当然,这只是实现同一目标的众多方法之一。在我看来,它比在bash中实现完整的排序算法更简单,在某些情况下它会更快。它处理所有特殊字符,包括换行符,并且应该适用于大多数常见系统。最重要的是,它可能会教你一些关于bash的新东西和令人敬畏的东西:)。

答案 7 :(得分:2)

试试这个:

echo ${array[@]} | awk 'BEGIN{RS=" ";} {print $1}' | sort

输出将是:

3
5
a
b
c
f

问题解决了。

答案 8 :(得分:2)

min sort:

#!/bin/bash
array=(.....)
index_of_element1=0

while (( ${index_of_element1} < ${#array[@]} )); do

    element_1="${array[${index_of_element1}]}"

    index_of_element2=$((index_of_element1 + 1))
    index_of_min=${index_of_element1}

    min_element="${element_1}"

        for element_2 in "${array[@]:$((index_of_element1 + 1))}"; do
            min_element="`printf "%s\n%s" "${min_element}" "${element_2}" | sort | head -n+1`"      
            if [[ "${min_element}" == "${element_2}" ]]; then
                index_of_min=${index_of_element2}
            fi
            let index_of_element2++
        done

        array[${index_of_element1}]="${min_element}"
        array[${index_of_min}]="${element_1}"

    let index_of_element1++
done

答案 9 :(得分:1)

array=(a c b f 3 5)
new_array=($(echo "${array[@]}" | sed 's/ /\n/g' | sort))    
echo ${new_array[@]}

new_array的echo内容将是:

3 5 a b c f

答案 10 :(得分:1)

This question看起来密切相关。顺便说一句,这是Bash中的一个mergesort(没有外部过程):

mergesort() {
  local -n -r input_reference="$1"
  local -n output_reference="$2"
  local -r -i size="${#input_reference[@]}"
  local merge previous
  local -a -i runs indices
  local -i index previous_idx merged_idx \
           run_a_idx run_a_stop \
           run_b_idx run_b_stop

  output_reference=("${input_reference[@]}")
  if ((size == 0)); then return; fi

  previous="${output_reference[0]}"
  runs=(0)
  for ((index = 0;;)) do
    for ((++index;; ++index)); do
      if ((index >= size)); then break 2; fi
      if [[ "${output_reference[index]}" < "$previous" ]]; then break; fi
      previous="${output_reference[index]}"
    done
    previous="${output_reference[index]}"
    runs+=(index)
  done
  runs+=(size)

  while (("${#runs[@]}" > 2)); do
    indices=("${!runs[@]}")
    merge=("${output_reference[@]}")
    for ((index = 0; index < "${#indices[@]}" - 2; index += 2)); do
      merged_idx=runs[indices[index]]
      run_a_idx=merged_idx
      previous_idx=indices[$((index + 1))]
      run_a_stop=runs[previous_idx]
      run_b_idx=runs[previous_idx]
      run_b_stop=runs[indices[$((index + 2))]]
      unset runs[previous_idx]
      while ((run_a_idx < run_a_stop && run_b_idx < run_b_stop)); do
        if [[ "${merge[run_a_idx]}" < "${merge[run_b_idx]}" ]]; then
          output_reference[merged_idx++]="${merge[run_a_idx++]}"
        else
          output_reference[merged_idx++]="${merge[run_b_idx++]}"
        fi
      done
      while ((run_a_idx < run_a_stop)); do
        output_reference[merged_idx++]="${merge[run_a_idx++]}"
      done
      while ((run_b_idx < run_b_stop)); do
        output_reference[merged_idx++]="${merge[run_b_idx++]}"
      done
    done
  done
}

declare -ar input=({z..a}{z..a})
declare -a output

mergesort input output

echo "${input[@]}"
echo "${output[@]}"

答案 11 :(得分:1)

如果可以为数组中的每个元素计算一个唯一的整数,如下所示:

tab='0123456789abcdefghijklmnopqrstuvwxyz'

# build the reversed ordinal map
for ((i = 0; i < ${#tab}; i++)); do
    declare -g ord_${tab:i:1}=$i
done

function sexy_int() {
    local sum=0
    local i ch ref
    for ((i = 0; i < ${#1}; i++)); do
        ch="${1:i:1}"
        ref="ord_$ch"
        (( sum += ${!ref} ))
    done
    return $sum
}

sexy_int hello
echo "hello -> $?"
sexy_int world
echo "world -> $?"

然后,你可以使用这些整数作为数组索引,因为Bash总是使用稀疏数组,所以不必担心未使用的索引:

array=(a c b f 3 5)
for el in "${array[@]}"; do
    sexy_int "$el"
    sorted[$?]="$el"
done

echo "${sorted[@]}"
  • 赞成。快。
  • 缺点。合并重复的元素,并且无法将内容映射到32位唯一整数。

答案 12 :(得分:0)

a=(e b 'c d')
shuf -e "${a[@]}" | sort >/tmp/f
mapfile -t g </tmp/f

答案 13 :(得分:0)

对于空格和换行符的常见问题,有一种解决方法:

使用不在原始数组中的字符(例如$'\1'$'\4'或类似字符)。

此功能完成工作:

# Sort an Array may have spaces or newlines with a workaround (wa=$'\4')
sortarray(){ local wa=$'\4' IFS=''
             if [[ $* =~ [$wa] ]]; then
                 echo "$0: error: array contains the workaround char" >&2
                 exit 1
             fi

             set -f; local IFS=$'\n' x nl=$'\n'
             set -- $(printf '%s\n' "${@//$nl/$wa}" | sort -n)
             for    x
             do     sorted+=("${x//$wa/$nl}")
             done
       }

这将对数组进行排序:

$ array=( a b 'c d' $'e\nf' $'g\1h')
$ sortarray "${array[@]}"
$ printf '<%s>\n' "${sorted[@]}"
<a>
<b>
<c d>
<e
f>
<gh>

这会抱怨源数组包含变通方法字符:

$ array=( a b 'c d' $'e\nf' $'g\4h')
$ sortarray "${array[@]}"
./script: error: array contains the workaround char

描述

  • 我们设置了两个局部变量wa(变通方法char)和一个空IFS
  • 然后(使用ifs null)我们测试整个数组$*
  • 不包含任何问题[[ $* =~ [$wa] ]]
  • 如果是,请发出消息并发出错误信号:exit 1
  • 避免文件名扩展:set -f
  • 将IFS(IFS=$'\n')的新值设置为循环变量x和换行符var nl=$'\n')。
  • 我们打印收到的所有参数值(输入数组$@)。
  • 但我们会使用变通方法"${@//$nl/$wa}"替换任何新行。
  • 发送要排序的值sort -n
  • 并将所有已排序的值放回位置参数set --
  • 然后我们逐个分配每个参数(以保留换行符)。
  • 循环for x
  • 到新阵列:sorted+=(…)
  • 内部引号以保留任何现有换行符。
  • 将变通方法恢复为换行符"${x//$wa/$nl}"
  • 完成

答案 14 :(得分:0)

我不相信你在Bash中需要一个外部排序程序。

这是我对简单冒泡排序算法的实现。

function bubble_sort()
{   #
    # Sorts all positional arguments and echoes them back.
    #
    # Bubble sorting lets the heaviest (longest) element sink to the bottom.
    #
    local array=($@) max=$(($# - 1))
    while ((max > 0))
    do
        local i=0
        while ((i < max))
        do
            if [ ${array[$i]} \> ${array[$((i + 1))]} ]
            then
                local t=${array[$i]}
                array[$i]=${array[$((i + 1))]}
                array[$((i + 1))]=$t
            fi
            ((i += 1))
        done
        ((max -= 1))
    done
    echo ${array[@]}
}

array=(a c b f 3 5)
echo " input: ${array[@]}"
echo "output: $(bubble_sort ${array[@]})"

这将打印:

 input: a c b f 3 5
output: 3 5 a b c f

答案 15 :(得分:0)

array=(z 'b c'); { set "${array[@]}"; printf '%s\n' "$@"; } \
    | sort \
    | mapfile -t array; declare -p array
declare -a array=([0]="b c" [1]="z")
  • 打开内联函数 {...} 以获取一组新的位置参数(例如 $1$2 等)。
  • 将数组复制到位置参数。 (例如,set "${array[@]}" 会将第 n 个数组参数复制到第 n 个位置参数。注意引号保留可能包含在数组元素中的空格)。
  • 打印每个位置参数(例如,printf '%s\n' "$@" 会将每个位置参数打印在自己的行上。再次注意,引号会保留每个位置参数中可能包含的空格)。
  • 然后 sort 做它的事情。
  • 使用 mapfile 将流读入数组(例如,mapfile -t array 将每一行读入变量 array,而 -t 忽略每一行中的 \n)。
  • 转储数组以显示其已排序。

作为函数:

set +m
shopt -s lastpipe

sort_array() { 
    declare -n ref=$1
    set "${ref[@]}"
    printf '%s\n' "$@"
    | sort \
    | mapfile -t $ref
}

然后

array=(z y x); sort_array array; declare -p array
declare -a array=([0]="x" [1]="y" [2]="z")

我期待被所有 UNIX 大师撕成碎片! :)

答案 16 :(得分:-2)

sorted=($(echo ${array[@]} | tr " " "\n" | sort))

本着bash / linux的精神,我会为每一步管道最好的命令行工具。 sort执行主要工作,但需要输入由换行符而不是空格分隔,因此上面非常简单的管道就是:

回声数组内容 - &gt;用换行替换空格 - &gt;排序

$()是回应结果

($())是将&#34;回显结果&#34;在一个数组

注意:正如@ {{{{}}在comment中提到的另一个问题:

  

sorted =($(...))部分正在使用&#34; split和glob&#34;运营商。你应该关闭glob:set -f或set -o noglob或shopt -op noglob或像*这样的数组元素将扩展为文件列表。