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
変更する必要がある場合があります。stat
gstat
sort
gsort
cut
gcut
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 に固有のものだからです)。