Como classificar um array zsh por data de modificação?

Como classificar um array zsh por data de modificação?

Como classificar um array zsh por data de modificação?

files=( ~/a ~/b ~/c )
# how to sort files by date?

PS: Aqui está meu caso de uso exato, ( fzé quase 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.'
}

Responder1

É muito mais fácil se você classificar a lista ao construí-la. Mas se você não consegue…

Uma abordagem clássica é adicionar o critério de classificação aos dados, classificá-los e, em seguida, remover o lixo adicionado. Construa uma matriz contendo carimbos de data e hora e nomes de arquivos, de forma inequívoca e com carimbos de data e hora em um formato que possa ser classificado lexicograficamente. Classifique a matriz (usando oo sinalizador de expansão de parâmetro) e retire o prefixo. Você pode usar ostatmódulo para obter o horário de modificação do arquivo.

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}#*:})

O %Nformato para zstat (para obter carimbos de data e hora com resolução de nanossegundos) requer zsh ≥5,6. Se o seu zsh for mais antigo, remova-o e o código ainda funcionará, mas comparando os carimbos de data e hora com uma resolução de 1 segundo. Muitos sistemas de arquivos têm resolução de subsegundos, mas não acho que você possa obtê-lo com o statmódulo zsh em versões mais antigas do zsh.

Se o seu zsh for muito antigo, você poderá obter carimbos de data/hora mais precisos com o statutilitário emCoreutils GNU. Se você tiver, provavelmente também terá os outros coreutils GNU, então vou usá-los. Coreutils GNU normalmente estão presentes em Linux não embarcado, mas podem não estar em BSD ou macOS. No macOS, você pode instalá-los usando brew. Se os coreutils GNU não fizerem parte do sistema operacional base, pode ser necessário alterar statpara gstat, sortpara gsorte cutpara gcut.

if (($#files)); then
  sorted=(${(0@)"$(stat  --printf='%040.18Y:%n\0' "$files[@]" | sort -rz | cut -z -d':' -f2-)"})
else
  sorted=()
fi

Uma abordagem alternativa do zsh é construir um padrão que inclua todos os arquivos $filese muito mais. Classifique os arquivos que correspondem a esse padrão e filtre-os para incluir apenas os arquivos desejados. Você precisa construir todo o padrão more_files, o que nem sempre é prático.

more_files=(~/*(Om))
sorted=(${more_files:*files})

Responder2

Você pode usar uma abordagem semelhante aAquele:

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 nanossegundos requer zsh 5.6 ou mais recente).

Responder3

Como Jeff apontou, será mais fácil criar seu array classificado, como em:

set -A files $(ls -trd -- ~/a ~/b ~/c)

Aqui dividindo a saída do lscaractere $IFS(por padrão contendo SPC, TAB, LF e NUL). Para dividir apenas em LF (para poder trabalhar com nomes de arquivos que contenham caracteres SPC ou TAB (mas não LF obviamente)), você pode definir IFS=$'\n'ou usar o fsinalizador de expansão de parâmetro:

files=(${(f)"$(ls -trd -- $files)"})

(aqui também usando a sintaxe do estilo zsh array=(...)em vez do estilo ksh88, set -A array ...uma vez que essa sintaxe já é específica do zsh).

informação relacionada