zsh 配列を変更日順に並べ替えるにはどうすればいいですか?

zsh 配列を変更日順に並べ替えるにはどうすればいいですか?

zsh 配列を変更日順に並べ替えるにはどうすればいいですか?

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

PS: これが私の正確な使用例です (fzほぼ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.'
}

答え1

リストを作成するときに並べ替えると、はるかに簡単になりますでも、もしそれができないなら…

古典的なアプローチは、データにソート基準を追加し、それをソートし、追加された不要なものを削除することです。タイムスタンプとファイル名を含む配列を、曖昧さのない方法で、タイムスタンプを辞書順にソートできる形式で作成します。配列をソートします(o パラメータ拡張フラグ)の場合は、プレフィックスを削除します。statファイルの変更時刻を取得するモジュール。

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

zstat のフォーマット%N(ナノ秒の解像度でタイムスタンプを取得する) には、zsh ≥5.6 が必要です。zsh が古い場合は、それを削除してもコードは動作しますが、タイムスタンプの比較は 1 秒の解像度で行われます。多くのファイルシステムには 1 秒未満の解像度がありますが、stat古いバージョンの zsh の zsh モジュールではそれができないと思います。

zshが古すぎる場合は、次statのユーティリティを使用してより正確なタイムスタンプを取得できます。GNU コアユーティリティをお持ちの場合は、おそらく他の GNU coreutils もお持ちなので、それらを使用します。GNU coreutils は、通常、非組み込み Linux に存在しますが、BSD や macOS には存在しない可能性があります。macOS では、 を使用してインストールできます。GNU coreutils が基本オペレーティング システムの一部でない場合は、をに、をに、 にbrew変更する必要がある場合があります。statgstatsortgsortcutgcut

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

zsh の別のアプローチとしては、 およびそれ以上のすべてのファイルを含むパターンを構築することです$files。このパターンに一致するファイルを並べ替え、目的のファイルのみを含むようにフィルタリングします。 のパターン全体を構築する必要がありますがmore_files、これは必ずしも実用的ではない場合があります。

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

答え2

次のようなアプローチも使えますそれです:

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ナノ秒の場合は zsh 5.6 以降が必要です)。

答え3

Jeff が指摘したように、次のようにソートされた配列を作成する方が簡単になります。

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

ここでは、文字 (デフォルトでは SPC、TAB、LF、NUL を含む) の出力を分割します。LFlsのみ$IFSで分割するには (SPC または TAB 文字 (もちろん LF は含まない) を含むファイル名を処理できるようにするため)、パラメータ拡張フラグを設定IFS=$'\n'または使用します。f

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

(ここでも、array=(...)ksh88 スタイルではなく zsh スタイルの構文を使用していますset -A array ...。この構文は、いずれにしても zsh に固有のものだからです)。

関連情報