对读入数组的元素进行排序
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)。
在将 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)。