¿Cómo ordenar una matriz zsh por fecha de modificación?
files=( ~/a ~/b ~/c )
# how to sort files by date?
PD: Aquí está mi caso de uso exacto ( fz
es casi 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.'
}
Respuesta1
Es mucho más fácil si ordenas la lista cuando la creas.. Pero si no puedes...
Un enfoque clásico es agregar el criterio de clasificación a los datos, luego ordenarlos y luego eliminar el material agregado. Cree una matriz que contenga marcas de tiempo y nombres de archivos, de una manera que no sea ambigua y con las marcas de tiempo en un formato que pueda ordenarse lexicográficamente. Ordene la matriz (usando elo
bandera de expansión de parámetros), luego elimine el prefijo. Puedes usar elstat
módulo para obtener la hora de modificación del archivo.
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}#*:})
El %N
formato zstat (para obtener marcas de tiempo con una resolución de nanosegundos) requiere zsh ≥5,6. Si su zsh es anterior, elimínelo y el código seguirá funcionando, pero comparando marcas de tiempo con una resolución de 1 segundo. Muchos sistemas de archivos tienen una resolución inferior a un segundo, pero no creo que puedas obtenerla con el stat
módulo zsh en versiones anteriores de zsh.
Si su zsh es demasiado antiguo, puede obtener marcas de tiempo más precisas con la stat
utilidad dePrincipales utilidades de GNU. Si lo tienes, probablemente también tengas las otras coreutils de GNU, así que las usaré. Las GNU coreutils suelen estar presentes en Linux no integrado, pero es posible que no estén en BSD o macOS. En macOS, puedes instalarlos usando brew
. Si las GNU coreutils no forman parte del sistema operativo base, es posible que deba cambiar stat
a gstat
, sort
a gsort
y cut
a gcut
.
if (($#files)); then
sorted=(${(0@)"$(stat --printf='%040.18Y:%n\0' "$files[@]" | sort -rz | cut -z -d':' -f2-)"})
else
sorted=()
fi
Un enfoque alternativo de zsh es crear un patrón que incluya todos los archivos $files
y más. Ordene los archivos que coincidan con este patrón, luego fíltrelo para incluir solo los archivos deseados. Es necesario crear el patrón completo para more_files
, lo que puede no siempre ser práctico.
more_files=(~/*(Om))
sorted=(${more_files:*files})
Respuesta2
Puede utilizar un enfoque similar aAquél:
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
para nanosegundos se requiere zsh 5.6 o posterior).
Respuesta3
Como señaló Jeff, será más fácil crear su matriz ordenada, como en:
set -A files $(ls -trd -- ~/a ~/b ~/c)
Aquí se divide la salida de ls
un $IFS
carácter (que por defecto contiene SPC, TAB, LF y NUL). Para dividir solo en LF (para poder trabajar con nombres de archivos que contienen caracteres SPC o TAB (pero no LF obviamente)), puede configurar IFS=$'\n'
o usar el f
indicador de expansión de parámetros:
files=(${(f)"$(ls -trd -- $files)"})
(aquí también se usa la sintaxis de estilo zsh array=(...)
en lugar de estilo ksh88, set -A array ...
ya que esa sintaxis ya es específica de zsh de todos modos).