logXX
のような名前のファイルが入ったディレクトリがあります。XXは、次のような2文字のゼロ詰めの大文字の16進数です。
log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...
一般的に言うと、合計で20または30ファイルよりも少なくなります。私の特定のシステムの日付と時刻は、信頼できるものではありません(信頼できるNTPまたはGPSタイムソースのない組み込みシステム)。ただし、ファイル名は上記のように確実に増加します。
特定のタイプの単一の最新のログエントリのすべてのファイルをgrep
したいのですが、次のようなファイルを一緒にcat
にまとめることを望んでいました...
cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1
ただし、bash
、sh
、zsh
などのバージョンが異なると、*
の展開方法が異なる可能性があることに気付きました。
man bash
ページには、*
の展開が、一致するファイル名のアルファベット順の昇順であるかどうかは記載されていません。私が利用できるすべてのシステムで試してみるたびに上昇しているように見えますが、それは定義された動作ですか、それとも実装固有のものですか?
言い換えると、cat /tmp/logs/log*
を完全に信頼して、すべてのログファイルをアルファベット順に連結できますか?
すべてのシェルで、グロブはデフォルトでソートされます。 彼らはすでに_/etc/glob
_ヘルパー によって70年代前半のUnixの最初のバージョンでグロブを拡張するためにケントンプソンのシェルによって呼び出されました(グロブに名前を付けました)。
sh
の場合、POSIXはそれらをstrcoll()
を使用してソートする必要があります。つまり、ls
のように、ユーザーのロケールでのソート順を使用していますstrcmp()
を使用して、バイト値のみに基づいています。
_$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log① log② lóg01
$ bash -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log② log① log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls | sort
log②
log①
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50
_
上記のように、ロケールに基づいてソートを行うシェルの場合、ここでは、GNU __en_GB.UTF-8
_ロケールのシステムでは、ファイル名の_-
_は無視されます)並べ替え用(ほとんどの句読文字がそうです)_ó
_は(少なくともイギリスの人々にとっては)より予想される方法で並べ替えられ、大文字小文字は無視されます(関係を決定する場合を除いて)。
ただし、log①log②にはいくつかの不整合があります。これは、①と②のソート順がGNUロケールで定義されていないためです(現在;うまくいけば、いつか修正される予定です)。同じソートなので、ランダムな結果が得られます。
ロケールを変更すると、並べ替え順序に影響します。ロケールをCに設定して、strcmp()
のような並べ替えを取得できます。
_$ bash -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log① log② lóg01
_
一部のロケールでは、すべてASCIIのすべての文字列であっても混乱が生じる可能性があることに注意してください。チェコのように(少なくともGNUシステム)で)ここでch
は照合要素でソートされますh
の後:
_$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch
_
または、@ ninjaljが指摘しているように、ハンガリー語のロケールではさらに奇妙なものです。
_$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy
_
zsh
では、 glob qualifiers でソートを選択できます。例えば:
_echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N) # to NOT sort
echo *(n) # sort by name, but numerically, and so on.
_
echo *(n)
の数値ソートは、numericglobsort
オプションを使用してグローバルに有効にすることもできます。
_$ zsh -c 'echo *'
log① log② log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log① log② log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50
_
あなたが(私がそうであったように)その特定のインスタンス(ここでは私の英国のロケールを使用)でその順序に混乱している場合は、詳細について ここ を参照してください。
Bashのマニュアルページでは、次のことを指定しています。
パス名拡張
Wordの分割後、
-f
オプションが設定されていない場合、bashは各Wordをスキャンして、文字*
、?
、および[
を探します。これらの文字のいずれかが表示される場合、Wordはパターンと見なされ、パターンに一致するファイル名のアルファベット順に並べ替えられたリストに置き換えられます[…]。
一部のシェルで特定のシェルオプションをトリガーしない限り、出力は同じであることが保証されます。
順序は POSIX標準 で指定されます。
パターンが既存のファイル名またはパス名に一致する場合、パターンはそれらのファイル名およびパス名に置き換えられ、現在のロケールで有効な照合シーケンスに従ってソートされます。この照合シーケンスにすべての文字の順序付けがない場合(XBD LC_COLLATEを参照)、同等に照合するファイル名またはパス名は、POSIXロケールの照合シーケンスを使用してバイトごとにさらに比較する必要があります。
POSIXロケールのLC_COLLATEカテゴリ も参照してください。つまり、LC_COLLATE=C
の場合、ASCIIの順序で並べられます。
bash
マニュアルでの言及
LC_COLLATE
この変数は、パス名展開の結果をソートするときに使用される照合順序を決定し、パス名展開とパターンマッチング内の範囲式、等価クラス、および照合シーケンスの動作を決定します。
ksh93
とzsh
も同様の表現を持っているため、この点についてはPOSIX標準に従っていると私は思います。
pdksh
やdash
のような他のシェルは、ファイル名の展開から生じるファイル名のソートについては何も述べていません。これは、少なくともPOSIXロケールを使用している場合は、同じ標準に準拠していることを意味します。私の経験では、ASCII filenamesのあからさまな)ソートを実行するシェルに出会ったことはありません。
主な目的が入力ファイルを古い順に並べ替えることである場合、次のように書くことができます
(cd /tmp/logs; cat `ls -rt log*`) | grep whatever
また、ローテーションおよび圧縮されたログも含まれる場合:
(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever