对读入数组的元素进行排序

sort elements read into array

在将 find 结果读入数组时,我希望它们同时排序(mp3,因此按曲目编号,这是文件名的第一部分),并认为应该这样做诀窍:

mp3s=()
while read -r -d $'[=10=]'; do
    mp3s+=("$REPLY")
done < <(sort <(find "$mp3Dir" -type f -name '*.mp3' -print0))

但数组中的元素从未正确排序(按文件名的第一部分,即 mp3 曲目编号:01_...、02_...、03_...等)

虽然下面的工作完成了,但似乎不必要地尴尬:

mp3s=()
while read -r -d $'[=11=]'; do
    mp3s+=("$REPLY")
done < <(find "$mp3Dir" -type f -name '*.mp3' -print0)

mp3s=( $(for f in "${mp3s[@]}" ; do
    echo "$f"
done | sort) )

必须有一种更精简的方法来完成这项工作,与我在第一个示例中的想法类似,不是吗?我已经尝试通过 find 命令两侧的 sort 进行阅读,使用它的众多选项进行排序(-n、-d 等),但没有任何运气(到目前为止)。

那么,在最初填充数组时,是否有更有效的方法来合并 sort 命令?

默认情况下,sort 假定记录以换行符分隔。然而,对 find 的调用指定了以 nul 分隔的输出。解决办法是在sort中加入-z标志。这告诉 sort 期望以 nul 分隔的输入并产生以 nul 分隔的输出。因此,尝试:

mp3s=()
while read -r -d $'[=10=]'; do
    mp3s+=("$REPLY")
done < <(sort -z <(find "$mp3Dir" -type f -name '*.mp3' -print0))

例子

假设我们有这些 mp3 文件:

$ find "." -type f -name '*.mp3' -print0
./music1/d b2.mp3./music1/a b1.mp3./music1/a b2.mp3./music1/d b1.mp3./music1/a b3.mp3./music1/d b3.mp3

首先,尝试 sort:

$ sort <(find "." -type f -name '*.mp3' -print0)
./music1/d b2.mp3./music1/a b1.mp3./music1/a b2.mp3./music1/d b1.mp3./music1/a b3.mp3./music1/d b3.mp3

文件保持无序。

现在,尝试 sort -z:

$ sort -z <(find "." -type f -name '*.mp3' -print0)
./music1/a b1.mp3./music1/a b2.mp3./music1/a b3.mp3./music1/d b1.mp3./music1/d b2.mp3./music1/d b3.mp3

文件现在已经整理好了。

在内部对 bash 进行排序的一种方法是使用关联数组并将数据放在键中,而不是值中。

declare -A mp3s=()
while IFS= read -r -d ''; do
    mp3s[$REPLY]=1
done < <(find "$mp3Dir" -type f -name '*.mp3' -print0)

...然后,迭代值:

for mp3 in "${!mp3s[@]}"; do
  printf '%q\n' "$mp3"
done

由于关联数组是 bash 4.0 中添加的一项功能,请注意此功能在 3.2 中不可用(某些圈子仍在使用,尤其是 MacOS)。