如何按修改日期对 zsh 数组进行排序?
files=( ~/a ~/b ~/c )
# how to sort files by date?
PS:这是我的确切用例,(fz
几乎fzf
)
v () {
local files
files=()
command rg '^>' ~/.viminfo | cut -c3- | while read line
do
[ -f "${line/\~/$HOME}" ] && files+="$line"
done
test -f ~/.emacs.d/.cache/recentf && {
command rg --only-matching --replace '$1' '^\s*"(.*)"$' ~/.emacs.d/.cache/recentf | while read line
do
[ -f "$line" ] && files+="$line"
done
}
files="$(<<<"${(F)files}" fz --print0 --query "$*")" || return 1
files="${files//\~/$HOME}"
local ve="$ve"
test -z "$ve" && ! isSSH && ve=nvim
"${ve:-vim}" -p ${(0@)files}
: '-o opens in split view, -p in tabs. Use gt, gT, <num>gt to navigate tabs.'
}
答案1
如果在构建列表时对列表进行排序会容易得多。但如果你不能……
一种经典的方法是向数据添加排序标准,然后对其进行排序,然后删除添加的垃圾。以明确的方式构建一个包含时间戳和文件名的数组,并且时间戳的格式可以按字典顺序排序。对数组进行排序(使用o
参数扩展标志),然后去掉前缀。您可以使用stat
模块获取文件的修改时间。
zmodload zsh/stat
for ((i=1; i<$#files; i++)); do times[$i]=$(stat -g -F %020s%N +mtime -L -- $files[$i]):$files[$i]; done
sorted=(${${(o)times}#*:})
zstat 的格式%N
(以纳秒分辨率获取时间戳)要求 zsh ≥5.6。如果您的 zsh 较旧,请将其删除,代码仍然可以工作,但以 1 秒的分辨率比较时间戳。许多文件系统都有亚秒分辨率,但我认为您无法使用stat
旧版本 zsh 中的 zsh 模块获得它。
如果您的 zsh 太旧,您可以使用该stat
实用程序获取更精确的时间戳GNU 核心工具。如果您有它,您可能也有其他 GNU coreutils,所以我将使用它们。 GNU coreutils 通常存在于非嵌入式 Linux 上,但可能不存在于 BSD 或 macOS 上。在 macOS 上,您可以使用brew
.如果 GNU coreutils 不是基本操作系统的一部分,您可能需要更改stat
为gstat
、sort
togsort
和cut
to gcut
。
if (($#files)); then
sorted=(${(0@)"$(stat --printf='%040.18Y:%n\0' "$files[@]" | sort -rz | cut -z -d':' -f2-)"})
else
sorted=()
fi
另一种 zsh 方法是构建一个包含所有文件$files
以及更多内容的模式。对匹配此模式的文件进行排序,然后对其进行过滤以仅包含所需的文件。您确实需要为 构建整个模式more_files
,这可能并不总是实用的。
more_files=(~/*(Om))
sorted=(${more_files:*files})
答案2
您可以使用类似于那个:
zmodload zsh/stat
array=(file1 file2...)
# store mtimes in an associative array:
typeset -A mtime
stat -nLF %s.%N -A mtime +mtime -- $array
# sort using the Oe glob qualifier
sorted_array=(/(e['reply=($array)']nOe['REPLY=$mtime[$REPLY]'])
(%N
纳秒需要 zsh 5.6 或更高版本)。
答案3
正如 Jeff 指出的,创建排序数组会更容易,如下所示:
set -A files $(ls -trd -- ~/a ~/b ~/c)
ls
这里分割on字符的输出$IFS
(默认包含 SPC、TAB、LF 和 NUL)。要仅按 LF 进行分割(以便能够使用包含 SPC 或 TAB 字符的文件名(但显然不是 LF)),您可以设置IFS=$'\n'
或使用f
参数扩展标志:
files=(${(f)"$(ls -trd -- $files)"})
(这里也使用 zsh 风格的array=(...)
语法而不是 ksh88 风格,set -A array ...
因为无论如何该语法已经是 zsh 特定的)。