シェルで、ディレクトリに作成された最新のファイルをどのようにtail
できますか?
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 head
とtail
でさえ、ヌル区切りの入力で動作させるためのスイッチを受け入れません。代わりに、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
にオプションが指定されなくなり、以降のすべてがファイル名になるため、_-
_で始まるファイル名を安全に処理できます。
tail `ls -t | head -1`
スペースを含むファイル名が心配な場合は、
tail "`ls -t | head -1`"
ウォッチで使用できるファイルサイズの変更を確認したいだけです。
watch -d ls -l
以下を使用できます。
_tail $(ls -1t | head -1)
_
$()
構文は、サブシェルを開始し、コマンド_ls -1t
_(時間順にすべてのファイルを1行に1つずつ一覧表示)を実行し、_head -1
_を介してパイプして最初の行を取得します(ファイル)。
次に、そのコマンドの出力(最新のファイル)がtail
に渡されて処理されます。
これが作成された最新のディレクトリエントリである場合、ディレクトリを取得するリスクがあることに注意してください。私はエイリアスでそのトリックを使用して、それらのログファイルのみを含むディレクトリ内の(ローテーションセットからの)最新のログファイルを編集しました。
POSIXシステムでは、「最後に作成された」ディレクトリエントリを取得する方法はありません。各ディレクトリエントリにはatime
、mtime
、ctime
がありますが、Microsoft Windowsとは異なり、 ctime
はCreationTimeを意味しません、「最後のステータス変更の時間」。
したがって、他の回答で説明されている「最後に最近変更されたファイルをテールする」ことが最善の方法です。私はこのコマンドに行きます:
tail -f "$(ls -tr | sed 1q)"
ls
コマンドを囲む引用符に注意してください。これにより、スニペットはほとんどすべてのファイル名で機能します。
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
を使用しません。
これを行う方法はおそらく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
シンプルな:
tail -f /path/to/directory/*
私にとってはうまくいきます。
問題は、tailコマンドを開始した後に生成されるファイルを取得することです。しかし、それが必要ない場合は(上記のすべてのソリューションはそれを考慮しないため)、アスタリスクは単純なソリューションであるIMOです。
tail`ls -tr | tail -1`
誰かがそれを投稿し、何らかの理由でそれを消去したが、これが機能する唯一のものなので、...
tail -f `ls -tr | tail`
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`
説明:
tail "$(ls -1tr|tail -1)"