Wie sortiert man ein Zsh-Array nach Änderungsdatum?
files=( ~/a ~/b ~/c )
# how to sort files by date?
PS: Hier ist mein genauer Anwendungsfall ( fz
ist fast 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.'
}
Antwort1
Es ist viel einfacher, wenn Sie die Liste beim Erstellen sortierenAber wenn Sie nicht können …
Ein klassischer Ansatz besteht darin, den Daten das Sortierkriterium hinzuzufügen, sie dann zu sortieren und dann den hinzugefügten Ballast zu entfernen. Erstellen Sie ein Array mit Zeitstempeln und Dateinamen in einer eindeutigen Weise und mit den Zeitstempeln in einem Format, das lexikografisch sortiert werden kann. Sortieren Sie das Array (mit demo
Parametererweiterungsflag), dann entfernen Sie das Präfix. Sie können diestat
Modul, um die Änderungszeit der Datei zu erhalten.
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}#*:})
Das %N
Format für zstat (um Zeitstempel mit einer Auflösung von Nanosekunden zu erhalten) erfordert zsh ≥5.6. Wenn Ihr zsh älter ist, entfernen Sie es und der Code funktioniert weiterhin, vergleicht jedoch Zeitstempel mit einer Auflösung von 1 Sekunde. Viele Dateisysteme haben eine Auflösung von unter einer Sekunde, aber ich glaube nicht, dass Sie diese mit dem zsh- stat
Modul in älteren Versionen von zsh erreichen können.
Wenn Ihr zsh zu alt ist, können Sie genauere Zeitstempel mit dem stat
Dienstprogramm von erhaltenGNU-Kerndienstprogramme. Wenn Sie es haben, haben Sie wahrscheinlich auch die anderen GNU-Coreutils, daher werde ich diese verwenden. GNU-Coreutils sind normalerweise auf nicht eingebettetem Linux vorhanden, aber möglicherweise nicht auf BSD oder macOS. Auf macOS können Sie sie mit installieren brew
. Wenn die GNU-Coreutils nicht Teil des Basisbetriebssystems sind, müssen Sie möglicherweise stat
in gstat
, sort
in gsort
und cut
in ändern gcut
.
if (($#files)); then
sorted=(${(0@)"$(stat --printf='%040.18Y:%n\0' "$files[@]" | sort -rz | cut -z -d':' -f2-)"})
else
sorted=()
fi
Ein alternativer zsh-Ansatz besteht darin, ein Muster zu erstellen, das alle Dateien in $files
und mehr enthält. Sortieren Sie die Dateien, die diesem Muster entsprechen, und filtern Sie es dann, um nur die gewünschten Dateien einzuschließen. Sie müssen das gesamte Muster für erstellen more_files
, was nicht immer praktikabel ist.
more_files=(~/*(Om))
sorted=(${more_files:*files})
Antwort2
Sie können einen Ansatz verwenden, der dem vonDas hier:
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
für Nanosekunden ist zsh 5.6 oder neuer erforderlich).
Antwort3
Wie Jeff betonte, ist es einfacher, Ihr Array sortiert zu erstellen, wie in:
set -A files $(ls -trd -- ~/a ~/b ~/c)
Hier wird die Ausgabe ls
nach $IFS
Zeichen aufgeteilt (standardmäßig enthält sie SPC, TAB, LF und NUL). Um nur nach LF aufzuteilen (um mit Dateinamen arbeiten zu können, die SPC- oder TAB-Zeichen enthalten (aber offensichtlich nicht LF)), können Sie das Parametererweiterungsflag setzen IFS=$'\n'
oder verwenden :f
files=(${(f)"$(ls -trd -- $files)"})
(hier wird auch die Syntax im zsh-Stil array=(...)
anstelle der im ksh88-Stil verwendet set -A array ...
, da diese Syntax ohnehin bereits zsh-spezifisch ist).