web-dev-qa-db-ja.com

ディレクトリ内の最新のファイルを「テール」にする方法

シェルで、ディレクトリに作成された最新のファイルをどのようにtailできますか?

20

Lsの出力を解析するしない! lsの出力の解析は difficult and unreliable です。

これを行う必要がある場合は、findを使用することをお勧めします。もともとはここにソリューションの要旨を示すための簡単な例がありましたが、この回答はやや人気があるようで、すべての入力でコピー/貼り付けして使用しても安全なバージョンを提供するようにこれを修正することにしました。あなたは快適に座っていますか?現在のディレクトリにある最新のファイルを提供するonelinerから始めます。

_tail -- "$(find . -maxdepth 1 -type f -printf '%T@.%p\0' | sort -znr -t. -k1,2 | while IFS= read -r -d '' -r record ; do printf '%s' "$record" | cut -d. -f3- ; break ; done)"
_

今はかなりワンライナーではないですよね?ここでも、シェル関数として、読みやすくするためにフォーマットされています。

_latest-file-in-directory () {
    find "${@:-.}" -maxdepth 1 -type f -printf '%T@.%p\0' | \
            sort -znr -t. -k1,2 | \
            while IFS= read -r -d '' -r record ; do
                    printf '%s' "$record" | cut -d. -f3-
                    break
            done
}
_

そして今thatをワンライナーとして:

_tail -- "$(latest-file-in-directory)"
_

他のすべてが失敗した場合は、上記の関数を_.bashrc_に含めて、問題を解決することを検討してください。仕事をやりたいだけの場合は、これ以上読む必要はありません。

これに関する警告は、1つ以上の改行で終わるファイル名がtailに正しく渡されないことです。この問題の回避は複雑であり、そのような悪意のあるファイル名が発生した場合、危険ではなく、「そのようなファイルがありません」エラーが発生するという比較的安全な動作が発生することで十分だと思います。

ジューシーなディテール

好奇心が強い人にとって、これは、それがどのように機能するか、なぜそれが安全で、なぜ他の方法がおそらく安全でないのかという退屈な説明です。

危険、ウィル・ロビンソン

まず、ファイルパスを区切るのに安全な唯一のバイトはnullです。これは、Unixシステムのファイルパスで一般的に禁止されている唯一のバイトだからです。ファイルパスのリストを処理するときは、区切り文字としてnullのみを使用し、1つのプログラムから別のプログラムにファイルパスを渡すときは、任意のバイトを詰まらせない方法で行うことが重要です。これと、ファイル名に改行やスペースが含まれていないと(偶然に)想定することで失敗する他の問題を解決する、一見正しいと思われる多くの方法があります。どちらの仮定も安全ではありません。

今日の目的のステップ1は、検索からファイルのnull区切りのリストを取得することです。 GNUのようなfindが_-print0_をサポートしている場合、これは非常に簡単です。

_find . -print0
_

しかし、このリストでは、どれが最新かはわかりません。そのため、その情報を含める必要があります。 findの_-printf_スイッチを使用することを選択します。これにより、出力に表示するデータを指定できます。 findのすべてのバージョンが_-printf_をサポートしているわけではありません(標準ではありません)が、GNU findはサポートしています。 _-printf_がない場合は、_-exec stat {} \;_に依存する必要があります。その時点で、statも標準ではないため、移植性のすべての希望を放棄する必要があります。とりあえず、GNUツールがあると仮定して次に進みます。

_find . -printf '%T@.%p\0'
_

ここでは、printf形式_%T@_を求めています。これは、Unixエポックの開始からの変更時間(秒)の後にピリオドが続き、その後に秒の端数を示す数値が続きます。この別のピリオドに追加し、次に_%p_(ファイルへの完全パス)を追加してから、ヌルバイトで終了します。

今私が持っています

_find . -maxdepth 1 \! -type d -printf '%T@.%p\0'
_

言うまでもないかもしれませんが、完全にするために_-maxdepth 1_はfindがサブディレクトリの内容をリストするのを防ぎ、_\! -type d_はtailにしたくないディレクトリをスキップします。これまでのところ、現在のディレクトリに変更時刻情報を含むファイルがあるので、変更時刻で並べ替える必要があります。

正しい順序で取得する

デフォルトでは、sortは、その入力が改行区切りのレコードであると想定しています。 GNU sortがある場合、_-z_スイッチを使用して、代わりにnull区切りのレコードを期待するように要求できます。;標準のsortの場合、解決策はありません。私は最初の2つの数値(秒と1秒未満)によるソートのみに関心があり、実際のファイル名によるソートはしたくないので、sortに2つのことを伝えます:まず、ピリオド(_._)レコードを並べ替える方法を検討するときに最初と2番目のフィールドのみを使用するフィールド区切り文字と2番目。

_| sort -znr -t. -k1,2
_

まず、一緒に価値のない3つの短いオプションをバンドルしています。 _-znr_は、_-z -n -r_)を簡潔に表す方法です。その後、_-t ._(スペースはオプション)はsortにフィールド区切り文字を通知し、_-k 1,2_はフィールド番号を指定します:最初と2番目(sortはフィールドをゼロからではなく1からカウントします)。現在のディレクトリのサンプルレコードは次のようになります。

_1000000000.0000000000../some-file-name
_

つまり、このレコードを注文するとき、sortは最初に_1000000000_を調べ、次に_0000000000_を調べます。 _-n_オプションは、両方の値が数値であるため、これらの値を比較するときに数値比較を使用するようにsortに指示します。数値は固定長なので重要ではありませんが、害はありません。

sortに与えられたもう1つのスイッチは、「リバース」を表す_-r_です。デフォルトでは、数値ソートの出力は最小の数値が最初になり、_-r_はそれを変更して、最小の数値を最後に、最大の数値を最初にリストします。これらの数値はタイムスタンプであるため、高いほど新しいことを意味し、これにより最新のレコードがリストの先頭に配置されます。

重要なビットだけ

sortからファイルパスのリストが表示されると、最上部に探している望ましい答えが表示されます。残っているのは、他のレコードを破棄してタイムスタンプを取り除く方法を見つけることです。残念ながら、GNU headtailでさえ、ヌル区切りの入力で動作させるためのスイッチを受け入れません。代わりに、whileループを一種の貧乏人のheadとして使用します。

_| while IFS= read -r -d '' record
_

まず、IFSの設定を解除して、ファイルのリストがWord分割の影響を受けないようにします。次に、readに2つのことを伝えます。入力(_-r_)のエスケープシーケンスを解釈しないでください。入力はnullバイト(_-d_);で区切られます。ここでは、空の文字列_''_は、「区切り文字なし」またはnullで区切られていることを示すために使用されます。各レコードは変数recordに読み込まれ、whileループが繰り返されるたびに、単一のタイムスタンプと単一のファイル名が含まれます。 _-d_はGNU拡張であることに注意してください。標準のreadしかない場合、この手法は機能せず、頼りになることはほとんどありません。

record変数には3つの部分があり、すべてピリオド文字で区切られています。 cutユーティリティを使用すると、それらの一部を抽出できます。

_printf '%s' "$record" | cut -d. -f3-
_

ここでは、レコード全体がprintfに渡され、そこからcutにパイプされます。 bashでは、パフォーマンスを向上させるために here string to _cut -d. -3f- <<<"$record"_を使用してこれをさらに簡略化できます。 cutに2つのことを伝えます。最初に_-d_を使用して、フィールドを識別するための特定の区切り文字を指定する必要があります(sortと同様に、区切り文字_._が使用されます)。 2番目のcutは、特定のフィールドの値のみを出力するように_-f_で指示されます。フィールドリストは、範囲_3-_として指定されます。これは、3番目のフィールドとそれに続くすべてのフィールドの値を示します。つまり、cutは、レコードで見つかった2番目の_._までをすべて読み取り、無視して、残りの部分(ファイルパス部分)を出力します。

最新のファイルパスを出力したら、続ける必要はありません。breakは、2番目のファイルパスに移動せずにループを終了します。

残っているのは、このパイプラインによって返されたファイルパスでtailを実行することだけです。私の例では、パイプラインをサブシェルで囲むことでこれを実行したことに気付いたかもしれません。気づかなかったかもしれませんが、サブシェルを二重引用符で囲みました。最後に、ファイル名に対して安全であるようにこのすべての努力を行っても、引用符で囲まれていないサブシェルの展開により、依然として問題が発生する可能性があるため、これは重要です。興味があれば 詳細な説明 を利用できます。 tailの呼び出しの2番目に重要ですが見落とされがちな側面は、ファイル名を展開する前に、オプション_--_を提供したことです。これにより、tailにオプションが指定されなくなり、以降のすべてがファイル名になるため、_-_で始まるファイル名を安全に処理できます。

26
phogg
tail `ls -t | head -1`

スペースを含むファイル名が心配な場合は、

tail "`ls -t | head -1`"
22
Pointy

ウォッチで使用できるファイルサイズの変更を確認したいだけです。

watch -d ls -l
4
tpal

以下を使用できます。

_tail $(ls -1t | head -1)
_

$()構文は、サブシェルを開始し、コマンド_ls -1t_(時間順にすべてのファイルを1行に1つずつ一覧表示)を実行し、_head -1_を介してパイプして最初の行を取得します(ファイル)。

次に、そのコマンドの出力(最新のファイル)がtailに渡されて処理されます。

これが作成された最新のディレクトリエントリである場合、ディレクトリを取得するリスクがあることに注意してください。私はエイリアスでそのトリックを使用して、それらのログファイルのみを含むディレクトリ内の(ローテーションセットからの)最新のログファイルを編集しました。

4
user53528

POSIXシステムでは、「最後に作成された」ディレクトリエントリを取得する方法はありません。各ディレクトリエントリにはatimemtimectimeがありますが、Microsoft Windowsとは異なり、 ctime はCreationTimeを意味しません、「最後のステータス変更の時間」。

したがって、他の回答で説明されている「最後に最近変更されたファイルをテールする」ことが最善の方法です。私はこのコマンドに行きます:

tail -f "$(ls -tr | sed 1q)"

lsコマンドを囲む引用符に注意してください。これにより、スニペットはほとんどすべてのファイル名で機能します。

4
Roland Illig

zsh内:

_tail *(.om[1])
_

参照: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers 、ここでmは変更時間_m[Mwhms][-|+]n_、および前のoは、1つの方法でソートされることを意味します(Oは、それを別の方法でソートします)。 _._は、通常のファイルのみを意味します。括弧内で_[1]_は最初の項目を選択します。 3つを選択するには_[1,3]_を使用し、最も古いものを使用するには_[-1]_を使用します。

ナイスショートで、lsを使用しません。

3
Anne van Rossum

これを行う方法はおそらく100万通りありますが、私が行う方法は次のとおりです。

tail `ls -t | head -n 1`

バックティックの間のビット(文字のような引用符)が解釈され、結果が末尾に返されます。

ls -t #gets the list of files in time order
head -n 1 # returns the first line only
1
iblamefish

シンプルな:

tail -f /path/to/directory/*

私にとってはうまくいきます。

問題は、tailコマンドを開始した後に生成されるファイルを取得することです。しかし、それが必要ない場合は(上記のすべてのソリューションはそれを考慮しないため)、アスタリスクは単純なソリューションであるIMOです。

1
bruno.braga
tail`ls -tr | tail -1`
0
user22644

誰かがそれを投稿し、何らかの理由でそれを消去したが、これが機能する唯一のものなので、...

tail -f `ls -tr | tail`
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`

説明:

  • ls -lt:変更時刻でソートされたすべてのファイルとディレクトリのリスト
  • grep -v ^ d:ディレクトリを除外
  • head -2以降:必要なファイル名を解析します
0
amit
tail "$(ls -1tr|tail -1)"
0
user31894