按字母顺序对bash参数进行排序

4
我该如何按字母顺序排列bash参数?
$ ./script.sh bbb aaa ddd ccc

将其放入一个数组中,使我现在有一个数组{aaa,bbb,ccc,ddd}


您已经在 $@ 中拥有它们。 - PradyJord
是的,但它们没有排序。 - ehaydenr
是的,这不是正确的,请检查我的答案以进行更正 :) - PradyJord
4个回答

7

您可以进行以下操作:

A=( $(sort <(printf "%s\n" "$@")) )

printf "%s\n" "${A[@]}"
aaa
bbb
ccc
ddd

使用步骤如下:

  1. 对参数列表即"$@"进行排序
  2. 将排序后的输出存储在数组中
  3. 打印排序后的数组

我不理解第一步的目标:为什么不直接使用 declare -a A=($(sort <(printf "%s\n" "$@"))) 呢? - F. Hauri - Give Up GitHub
可以省略那一步,那只是为了演示目的。我可以补充说明答案的核心在第二步而不是第一步。 - anubhava
用户应该注意,这不适用于空格或特殊字符。 - that other guy
@rici 它根据IFS拆分结果,默认情况下包括空格。 - that other guy
1
@thatotherguy: sortargs() { mapfile -t $1 < <(printf "%s\n" "${@:2}"|sort); }; sortargs A "foo bar" "*" "" $'baz\tcow'; printf "%s\n" "${A[@]}". 当然,仍然无法处理换行符。 - rici

4

我希望以下两行内容能够帮到您。

sorted=$(printf '%s\n' "$@"|sort)

echo $sorted

这会给你一个已排序的命令行参数。不过我不知道为什么需要它 :)

但无论如何,它将对您的命令行进行排序

删除了不必要的内容。


0

由于似乎没有人欣赏我减少分支的努力,所以有一个比使用IFS解析和设置变量更好的解决方案。

第一部分:非常简短而强大的解决方案:

正如@rici在另一篇帖子的评论中建议的那样,我添加了-t参数到mapfile中:

mapfile -t args < <(sort < <(printf "%s\n" "$@"))

这也适用于空格。

示例:

#!/bin/bash

mapfile args < <(sort < <(printf "%s\n" "$@"))

mapfile -t args < <(sort < <(printf "%s\n" "$@"))

declare -p args

for (( i=0 ; i<${#args[@]} ;i++));do

    printf "%3d: %s\n" $i "${args[i]%$'\n'}"

    printf "%3d: %s\n" $i "${args[i]}"

done

运行示例:

/tmp/script ccc "a a" aaa ddd aa AA z aab
declare -a args='([0]="aa
" [1]="a a
" [2]="AA
" [3]="aaa
" [4]="aab
" [5]="ccc
" [6]="ddd
" [7]="z
")'
  0: aa
  1: a a
  2: AA
  3: aaa
  4: aab
  5: ccc
  6: ddd
  7: z

第二部分:非常快速:纯bash方式(无需forks!)

注意:当然,这不是更好、更健壮的排序方式,但在许多情况下,这可能会有效地使用。

由于(至少)一个人似乎更喜欢将a排在aa之前,因此现在将z替换为0

此示例仅限于1st 6个字符,但您可以将6替换为更大的数字,但要添加相同数量的z

#!/bin/bash

sep='§'
for i;do
    a=${i//[^a-zA-Z0-9]/0}000000
    args[36#${a:0:6}]+=${args[36#${a:0:6}]+$sep}${i}
  done

IFS=$sep args=(${args[*]})
printf "%s\n" ${args[@]}

declare -p args

为了区分大小写,您可以将36#替换为64#

示例代码:

#!/bin/bash

sep=§
base=64
chars=8
fillc=0
usage() {
  cat <<eousage
Usage: $0 [-ai] [-p precision] [-s inner separator]
    -a     for sorting \`\`empty'' After (\`\`aa'' after \`\`aaa'')
    -i     for case Insensitive
    -p NUM tell the number of characters to compare (default: $chars)
    -s SEP let you precise inner separator, (default \`\`$sep'')
eousage
}

while getopts "iap:s:" opt;do case $opt in
    a ) fillc=z ;;
    i ) base=36 ;;
    p ) chars=$OPTARG ;;
    s ) sep=$OPTARG ;;
    * ) usage ; exit 1 ;;
  esac ; done ;
shift $[OPTIND-1]

printf -v cfill "%${chars}s"
cfill=${cfill// /$fillc}
for i;do
    a=${i//[^a-zA-Z0-9]/$fillc}$cfill
    idx=$[$base#${a:0:$chars}]
    args[$idx]+=${args[$idx]+$sep}${i}
  done

declare -p args
IFS=$sep args=(${args[*]})
declare -p args

for (( i=0 ; i++<${#args[@]} ;b));do
    printf "%3d: %s\n" $i ${args[i-1]}
  done

运行测试用例:

/tmp/script ccc aaa ddd aa AA z aab
declare -a args='([44667659878400]="aa" [44678397296640]="aaa" 
    [44679471038464]="aab" [53614076755968]="ccc" [58081916485632]="ddd" 
    [153931627888640]="z" [160803575562240]="AA")'
declare -a args='([0]="aa" [1]="aaa" [2]="aab" [3]="ccc" [4]="ddd" 
    [5]="z" [6]="AA")'
  1: aa
  2: aaa
  3: aab
  4: ccc
  5: ddd
  6: z
  7: AA

不区分大小写:

/tmp/script -i ccc aaa ddd aa AA z aab
declare -a args='([805409464320]="aa§AA" [806014126080]="aaa" 
    [806074592256]="aab" [967216951296]="ccc" [1047818363904]="ddd" 
    [2742745743360]="z")'
declare -a args='([0]="aa" [1]="AA" [2]="aaa" [3]="aab" [4]="ccc" 
    [5]="ddd" [6]="z")'
  1: aa
  2: AA
  3: aaa
  4: aab
  5: ccc
  6: ddd

排序后为空

/tmp/script -ia ccc aaa ddd aa AA z aab
declare -a args='([806074592255]="aaa" [806135058431]="aab" 
    [807586246655]="aa§AA" [967277417471]="ccc" [1047878830079]="ddd" 
    [2821109907455]="z")'
declare -a args='([0]="aaa" [1]="aab" [2]="aa" [3]="AA" [4]="ccc" 
    [5]="ddd" [6]="z")'
  1: aaa
  2: aab
  3: aa
  4: AA
  5: ccc
  6: ddd
  7: z

精度:1个字符:

/tmp/script -iap1 ccc aaa ddd aa AA z aab
declare -a args='([10]="aaa§aa§AA§aab" [12]="ccc" [13]="ddd" [35]="z")'
declare -a args='([0]="aaa" [1]="aa" [2]="AA" [3]="aab" [4]="ccc" 
    [5]="ddd" [6]="z")'
  1: aaa
  2: aa
  3: AA
  4: aab
  5: ccc
  6: ddd
  7: z

和精度:10个字符:

/tmp/script -p 10 ccc aaa ddd aa AA z aab
declare -a args='([182958734861926400]="aa" [183002715327037440]="aaa" 
    [183007113373548544]="aab" [219603258392444928]="ccc" 
    [237903529925148672]="ddd" [630503947831869440]="z" 
    [658651445502935040]="AA")'
declare -a args='([0]="aa" [1]="aaa" [2]="aab" [3]="ccc" [4]="ddd" 
    [5]="z" [6]="AA")'
  1: aa
  2: aaa
  3: aab
  4: ccc
  5: ddd
  6: z
  7: AA

空格和其他字符:

/tmp/script -is @ ccc "a a" aaa ddd 'a*a' 'a§a' aa AA z aab
declare -a args='([784246302720]="a a@a*a@a§a" [805409464320]="aa@AA" 
    [806014126080]="aaa" [806074592256]="aab" [967216951296]="ccc" 
    [1047818363904]="ddd" [2742745743360]="z")'
declare -a args='([0]="a a" [1]="a*a" [2]="a§a" [3]="aa" [4]="AA" 
    [5]="aaa" [6]="aab" [7]="ccc" [8]="ddd" [9]="z")'
  1: a a
  2: a*a
  3: a§a
  4: aa
  5: AA
  6: aaa
  7: aab
  8: ccc
  9: ddd
 10: z

它将abc排序在a之前并且删除空元素。冒泡排序不是更简单和更准确吗? - that other guy
根据你正在做什么(排序),这可能是一种有趣的使用纯bash进行排序的方式! - F. Hauri - Give Up GitHub
1
@F.Hauri同意,这是一个写得很好的答案,没有理由就点踩是不应该的。+1来抵消这种损害。 - jaypal singh
@jaypal 谢谢(我不喜欢发布“感谢答案”评论,但是...;-)) - F. Hauri - Give Up GitHub
@thatotherguy 你错了:这并不是“不必要的”长,因为这是一种更简单的方法来完成这个任务,而且没有使用fork。而且我也没有“抄袭”这个评论,它是针对你(而不是我)的回答... - F. Hauri - Give Up GitHub
显示剩余2条评论

0

这里有一个调用,可以打破所有其他在此处提出的解决方案:

./script.sh "foo bar" "*" "" $'baz\ncow'

这是一段正常工作的代码:

array=()
(( $# )) && while IFS= read -r -d '' var
do
  array+=("$var")
done <  <(printf "%s\0" "$@" | sort -z)

我downvote了这个帖子,因为我不知道有很多排序长字段和换行真正需要的情况。另一方面,我知道有很多小字段可以使用快速排序的情况。 - F. Hauri - Give Up GitHub
@F.Hauri 这是一个写得很差的问题,没有提到边缘情况。我会给这个问题投反对票。 - jaypal singh
只要你出于公正和合理的原因而这样做,而不是出于小气幼稚的心态,我就没问题。 - that other guy
@jaypal 我同意,我不会对这个答案进行负评,因为它真正努力地提出了一种可靠的处理“以 null 结尾的字符串”的方法,但是 SO 现在不允许我更正我的投票... - F. Hauri - Give Up GitHub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接