
以下を含むディレクトリが提供されます。
note 1.txt
、昨日最後に修正されましたnote 2.txt
、最後の修正は私たちでしたnote 3.txt
、今日最後に修正されました
配列を取得する最良の方法は何ですかnote 3
note 1
note 2
?
「最高」を定義するために、私は効率性と移植性よりも堅牢性(macOSのZsh環境で)に興味があります。
意図されたユースケースは何百または何千ものプレーンテキストファイルを含むディレクトリですが(質問を混同する危険性があります)、これは私が経験しているより一般的な問題、つまりファイルパスで文字列を実行する特定のケースです。最良の方法はls
、find
およびなどのコマンドを介して印刷することですmdfind
。
私は上記の目的を達成するためにこのコマンドを呼び出すマクロを使用してきました。
ls -t | sed -e 's/.[^.]*$//'
決して失敗しませんが、
ソートされていないリストを生成する引数拡張find
(ファイルパスを安全に区切るには改行ではなく文字を使用)を使用してデフォルト名を抽出します。NUL
find . -type f -print0 | while IFS= read -d '' -r l ; do print "${${l%.*}##*/}" ; done
しかし、変更された日付で並べ替えるには呼び出しが必要なようで、stat
macOSsort
にはタグがfind
ないためです。-printf
そうでなければうまくいくかもしれません。。
最後にZshを使用してください。グローバル予選:
for f in *(om) ; do print "${f%.*}" ; done
移植可能ではありませんが、この最後のアプローチは私にとって最も強力で効率的なようです。これは正しいですfind
か?単にディレクトリにファイルを一覧表示するのではなく、実際に検索を実行するときに上記のコマンドの修正版を使用しないでください。
答え1
存在するzsh
、
list=(*(Nom:r))
確かに最も丈夫です。
print -rC1 -- *(Nom:r)
1行に1つずつ印刷する
print -rNC1 -- *(Nom:r)
NULはファイルパスに許可されていない唯一の文字であるため、出力ですべての操作を実行できるようにNULで区切られたレコードとして使用されます。
*(N-om:r)
修正時間を考慮するには、次のように変更します。後ろにシンボリックリンクの確認(対象のランタイム、そのようなシンボリックリンクではありませんls -Lt
)。
:r
(のため根csh
name)は、拡張を削除するために使用される履歴修飾子です。.bashrc
このオプションが有効な場合にのみ空の文字列になります。dotglob
再帰的に実行されるように変更**/*(N-om:t:r)
(:t
尾(デフォルト名)、つまりディレクトリコンポーネントを削除します。
任意のファイル名を使用してこれを確実に実行することはls
困難です。
1つのアプローチは、実行ls -td -- ./*
(ファイル名リストが引数リスト制限に準拠していると仮定)し、その出力を解析して各ファイル名がで始まるという事実に基づいて、./
NUL区切りリストまたはシェル引用符リストを生成してシェルに渡すことです。しかし、それは移植可能です。また、perl
またはで助けを求めない限り非常に痛いですpython
。
ただし、信頼できる場合は、perl
NULpython
で区切られた出力を使用してファイルのリストを生成してソートすることができます(秒未満の精度をサポートしたい場合は移植するのは簡単ではないかもしれません)。
ls -t | sed -e 's/.[^.]*$//'
改行文字を含むファイル名では正しく機能しません(IIRCの一部のバージョンのmacOSには/etc
デフォルトでこれらのファイル名が付属しています)。有効な文字を形成しないバイトシーケンスが含まれているファイル名の場合でも、失敗または.
一致しない[^.]
可能性があります。ただし、macOSでは機能しない可能性があり、ロケールをC
/POSIX
に設定して問題を解決できますsed
。
.
()はs/\.[^.]*$//
すべての文字に一致する正規表現演算子なので、エスケープする必要があります。それ以外の場合は、ドットのないファイルをfoobar
空の文字列に変換します。
文字列の印刷に注意してください。生の、それは:
print -r -- "$string"
print "$string"
$string
で始まる値は失敗し、-
コマンドインジェクションの脆弱性が発生しても(たとえば、ここでstring='-va[$(uname>&2)1]'
無害なuname
コマンドを使用しようとすると)文字を含む値を破壊します\
。
あなたの:
find . -type f -print0 | while IFS= read -d '' -r l ; do print "${${l%.*}##*/}" ; done
もう一つの問題は、あなたが服を脱いだということです.*
今後ディレクトリコンポーネントを削除します。たとえば、aは置き換え./foo.d/bar
られ、空のfoo
文字列にbar
なります。./foo
find
さまざまなシェルで出力を処理する安全な方法については、以下を参照してください。検索結果を繰り返すのはなぜ悪い習慣ですか?
答え2
IMNSHOの堅牢性とシェルスクリプトは互換性のない概念です(IFSは単なるハッキングです。申し訳ありません)。私はあなたが欲しいものを強力な方法で達成する方法は2つしかないと思います。通常の言語(Python、Cなど)でプログラムを書くか、堅牢性のために特別に設計されたツールを使用することです。
csv-nix-tools(*) を使用すると、次のようにこれを実現できます。
csv-ls -c name,mtime_sec,mtime_nsec |
csv-sort -c mtime_sec,mtime_nsec |
csv-cut -c name |
csv-add-split -c name -e . -n base,ext -r |
csv-cut -c base |
csv-header --remove
かなり自明です。
ファイルのデフォルト名だけを表示したい場合はこれで十分ですが、一般的に取得したデータを使用して便利なタスクを実行したい場合がよくあります。これがシンクツールの目的です。現在、csv-exec(各行でコマンドを実行)、csv-show(人が読める形式でデータをフォーマットする)、csv-plot(gnuplotを使用した2Dまたは3Dグラフィックスの作成)の3つがあります。
まだ不足している部分がありますが、ツールは使用を開始するのに十分です。
答え3
GNUツールを搭載したシステムで、かなり広範囲のksh拡張機能(bashとzshを含む)を使用するすべてのシェルで動作する他の方法がすでに扱われていないことに驚きました。
while IFS= read -r -d ' ' time && IFS= read -r -d '' filename; do
printf 'Filename %q, with epoch time %s\n' "$filename" "$time"
done < <(find . -mindepth 1 -maxdepth 1 -printf '%T@ %P\0' | sort -gz)
仕組みを説明してください。
- フォーマット
find
文字列は、%T@ %P\0
ファイルごとに10進タイムスタンプ(オプションの1秒未満の精度を含む)、スペース、ファイルのデフォルト名、およびNULを印刷します。 - では、浮動小数点値の一般化された順序が正しく処理され、区切り文字で改行文字の代わり
sort -gz
にNULが期待されます。-g
-z
- では、
IFS= read -r -d ' ' time && IFS= read -r -d '' filename
最初の空白で時間読み取りを終了し、最初のNULでファイル名の読み取りを終了します。 - フォーマット文字列を使用して結果を印刷するときに、
%q
ファイル名の印刷できない文字(タブ、改行、キャリッジリターンなど)も読みやすいテキストに変換します。