¿Cómo ordenar una matriz zsh por fecha de modificación?

¿Cómo ordenar una matriz zsh por fecha de modificación?

¿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 ( fzes 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 elstatmó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 %Nformato 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 statmódulo zsh en versiones anteriores de zsh.

Si su zsh es demasiado antiguo, puede obtener marcas de tiempo más precisas con la statutilidad 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 stata gstat, sorta gsorty cuta 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 $filesy 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]'])

( %Npara 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 lsun $IFScará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 findicador 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).

información relacionada