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 ostat
mó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 %N
formato 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 stat
mó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 stat
utilitá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 stat
para gstat
, sort
para gsort
e cut
para 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 $files
e 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]'])
( %N
para 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 ls
caractere $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 f
sinalizador 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).