如何按修改日期對 zsh 陣列進行排序?

如何按修改日期對 zsh 陣列進行排序?

如何按修改日期對 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 不是基本作業系統的一部分,您可能需要變更statgstatsorttogsortcutto 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 特定的)。

相關內容