Bashアスタリスク*ワイルドカードは常に(昇順)ソートされたリストを生成しますか?

Bashアスタリスク*ワイルドカードは常に(昇順)ソートされたリストを生成しますか?

同じ名前のファイルでいっぱいのディレクトリがあります。logXXここで、XXはゼロで埋められた大文字の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*すべてのログファイルをアルファベット順にリンクすることに絶対に依存できますか?

答え1

すべてのシェルでは、globはデフォルトでソートされます。彼らはすでに/etc/globアシスタントと一緒にいます1970年代初頭、Unixの最初のバージョンでは、glob(したがってglobという名前)を拡張するためにKen Thompsonのシェルによって呼び出されました。

POSIXでは、forなどのユーザーロケールのソート順を使用してshソートする必要がありますが、一部はまだバイト値のみでソートされます。strcoll()lsstrcmp()

$ 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

ロケールに基づいてソートされたシェルの場合、en_GB.UTF-8ロケールを持つGNUシステムでは-。もっと期待される方法で並べ替えó(少なくとも英国人の場合)、大文字と小文字を無視します(関係決定が関係しない限り)。

しかし、ログ①とログ②の間に若干の不一致があることがわかります。これは、①と②のソート順がGNUロケールで定義されていないためです(まだ、いつかは修正される予定です)。順番が同じなのでランダムな結果が出ます。

ロケールを変更すると、ソート順に影響します。strcmp()同様のソートを得るには、ロケールをCに設定できます。

$ 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文字列全体を使用しても、少し混乱する可能性があります。チェコch語と同様に(少なくともGNUシステムでは)要素の構成ソート後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ソートを選択できますグローバル予選。たとえば、

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

あなた(私のように)が特定の状況で注文について混乱している場合(ここで私のイギリスのロケールを使用して)、以下を参照してください。ここもっと学ぶ。

答え2

Bash のマニュアルページでは、以下を指定します。

パス名拡張

単語分割後、-fbashはこのオプションが設定されていない限り文字をスキャンし、*各単語を?スキャンします[。これらの文字のいずれかが発生すると、その単語はパターンとして扱われ、[...]パターンと一致するファイル名のアルファベット順のリストに置き換えられます。

答え3

特定のシェルで非常に特定のシェルオプションを実行しない限り、出力は同じであることが保証されます。

順序は次のように指定されます。POSIX規格:

パターンが既存のファイル名またはパス名と一致する場合、パターンはそのファイル名とパス名で置き換える必要があります。現在のロケールで有効な組み合わせ順序に基づいてソート。この組み合わせ順序にすべての文字の完全な組み合わせがない場合(XBD LC_COLLATEを参照)、同等に結合されたすべてのファイル名またはパス名は、POSIXロケールの組み合わせ順序を使用してバイト単位でさらに比較する必要があります。

また、見ることができますPOSIXロケールのLC_COLLATEカテゴリ、簡単に言えば、ifLC_COLLATE=CはASCIIの順序でソートされます。


マニュアルbashに記載されている

LC_COLLATE

この変数は、パス名拡張の結果をソートするときに使用される照合順序を決定し、範囲式、同等クラス、パス名拡張、およびパターン一致のソート順の動作を決定します。

ksh93そしてzsh同様の表現があり、これは彼らがこの点でPOSIX標準に準拠していると信じさせます。

pdkshなどの他のシェルは、dashファイル名のグロービングによるファイル名の順序を考慮しません。私は、これが少なくともPOSIXロケールを使用するとき、まだ同じ標準に従うことを意味すると信じたいと思います。私の経験では、まだASCIIファイル名の「奇妙な」順序を指定するシェルを見たことがありません。

答え4

主な目標が入力ファイルを年齢別にソートすることであれば、最も古いものから次のように書くことができます。

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

ログの回転と圧縮も関連する場合:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever

関連情報