Bash根据元素长度对数组进行排序?

PJ *_*ngh 9 bash array shell-script sort

给定一个字符串数组,我想根据每个元素的长度对数组进行排序。

例如...

    array=(
    "tiny string"
    "the longest string in the list"
    "middle string"
    "medium string"
    "also a medium string"
    "short string"
    )
Run Code Online (Sandbox Code Playgroud)

应该排序...

    "the longest string in the list"
    "also a medium string"
    "medium string"
    "middle string"
    "short string"
    "tiny string"
Run Code Online (Sandbox Code Playgroud)

(作为奖励,如果列表按字母顺序对相同长度的字符串进行排序会很好。在上面的示例medium string中,middle string即使它们的长度相同,也是在之前排序的。但这不是一个“硬”要求,如果它过于复杂解决方案)。

如果数组是就地排序的(即“数组”被修改)或者如果创建了一个新的排序数组,这都可以。

cho*_*oba 12

如果字符串不包含换行符,则以下应该有效。它按长度对数组的索引进行排序,使用字符串本身作为辅助排序标准。

#!/bin/bash
array=(
    "tiny string"
    "the longest string in the list"
    "middle string"
    "medium string"
    "also a medium string"
    "short string"
)
expected=(
    "the longest string in the list"
    "also a medium string"
    "medium string"
    "middle string"
    "short string"
    "tiny string"
)

indexes=( $(
    for i in "${!array[@]}" ; do
        printf '%s %s %s\n' $i "${#array[i]}" "${array[i]}"
    done | sort -nrk2,2 -rk3 | cut -f1 -d' '
))

for i in "${indexes[@]}" ; do
    sorted+=("${array[i]}")
done

diff <(echo "${expected[@]}") \
     <(echo "${sorted[@]}")
Run Code Online (Sandbox Code Playgroud)

请注意,转向真正的编程语言可以极大地简化解决方案,例如在 Perl 中,您只需

sort { length $b <=> length $a or $a cmp $b } @array
Run Code Online (Sandbox Code Playgroud)


Kus*_*nda 9

readarray -t array < <(
for str in "${array[@]}"; do
    printf '%d\t%s\n' "${#str}" "$str"
done | sort -k 1,1nr -k 2 | cut -f 2- )
Run Code Online (Sandbox Code Playgroud)

这将从进程替换中读取排序数组的值。

进程替换包含一个循环。循环输出数组的每个元素,前面加上元素的长度和中间的制表符。

环路的输出从最大数字顺序排序到最小(和如果按字母顺序的长度是相同的;使用-k 2r代替-k 2扭转字母顺序)和的结果的是被发送到cut哪个删除与字符串长度的列。

对测试脚本进行排序,然后进行测试运行:

array=(
    "tiny string"
    "the longest string in the list"
    "middle string"
    "medium string"
    "also a medium string"
    "short string"
)

readarray -t array < <(
for str in "${array[@]}"; do
    printf '%d\t%s\n' "${#str}" "$str"
done | sort -k 1,1nr -k 2 | cut -f 2- )

printf '%s\n' "${array[@]}"
Run Code Online (Sandbox Code Playgroud)
$ bash script.sh
the longest string in the list
also a medium string
medium string
middle string
short string
tiny string
Run Code Online (Sandbox Code Playgroud)

这假设字符串不包含换行符。在具有最新 的 GNU 系统上bash,您可以通过使用空字符而不是换行符作为记录分隔符来支持数据中的嵌入换行符:

readarray -d '' -t array < <(
for str in "${array[@]}"; do
    printf '%d\t%s\0' "${#str}" "$str"
done | sort -z -k 1,1nr -k 2 | cut -z -f 2- )
Run Code Online (Sandbox Code Playgroud)

在这里,数据\0在循环中使用尾随而不是换行符打印,sortcut通过它们的-zGNU 选项readarray读取以空分隔的行,最后使用 读取以空分隔的数据-d ''

  • 请注意,`-d '\0'` 实际上是 `-d ''`,因为 `bash` 不能将 NUL 字符传递给命令,即使是它的内置命令。但它确实将 `-d ''` 理解为 NUL_ 上的 _delimit。请注意,您需要 bash 4.4+。 (3认同)