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


53

私は、logXXXXが次のような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

しかし、bashor shまたはor zshなどの異なるバージョンが、どのように*展開されるかについて異なるアイデアを持っているかもしれないということが私には思いつきました。

このman bashページは、の拡張*が一致するファイル名のアルファベット順の完全に昇順のリストであるかどうかを示していません。利用可能なすべてのシステムで試してみるたびに上昇しているように見えますが、動作は定義済みですか、それとも実装固有ですか?

言い換えればcat /tmp/logs/log*、すべてのログファイルをアルファベット順に連結することに絶対に頼ることができますか?


1
@ADDBのデフォルトのソート順sortは、ファイル名のグロビングパターンを展開しているときのシェルのソート順と同じです。
クサラナナンダ

9
これはひどいファイル命名規則です。なぜlog(0)=-inftyで実行を開始するのですか?
EP

14
@EP私たちのファイルシステムは、iノードの超現実的な番号付けを備えた複雑な7次元ハイパートロイドです。busyboxのいくつかのあいまいなブランチと一緒に祖父にされており、私たちは今ウィットで立ち往生しています:)
Wossname

1
catwith grep -h pattern /tmp/logs/log*を使用して、一致するファイル名の前に追加しないようにすることができます。(少なくともGNU grepでは、POSIXまたはbusyboxをチェックしませんでした。)
Peter Cordes

1
@Kusalanandaの無駄な使用を聞いたことがありますがcat、これは無駄な使用ですsort

回答:


52

すべてのシェルで、globはデフォルトでソートされます。彼らはすでに、/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()-likeソートを取得できます。

$ 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のall-alnum文字列であっても混乱が生じる可能性があることに注意してください。チェコ語(少なくとも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

ではzshglob修飾子を使用してソートを選択できます。例えば:

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

あなたが(私がそうであったように)その特定のインスタンス(ここでは私の英国のロケールを使用)でその順序に混乱している場合、詳細についてはこちらを参照してください。


1
「ch」の場合はさらに奇妙です。一部のロケールでは、「ch」、「Ch」、および「CH」がそれぞれ1つの照合要素であり、「cH」が2つの照合要素であると判断できます。参照:unicode.org/cldr/trac/ticket/889現在のCLDRは完全に一貫していないようです。現在のハンガリー語(unicode.org/cldr/trac/browser/trunk/common/collat​​ion/hu.xml)には次のようなルールがあります。&C<cs<<<Cs<<<CS&C<cs<<<cS<<<Cs<<<CS提案された実験的ドラフトとしてマークされています。CLDRにインポートされたいくつかの古いデータから判断すると、古いAIXとMSは「小文字と大文字は2つの異なる照合要素」ビューを好むように思われました。
ninjalj

とにかく動作しなかったシステムを見てきました。:(
ジョシュア

38

bashのマニュアルページでは、次のことを指定しています。

パス名の展開

しない限り、単語の分割後に、-fオプションが設定されている、bashは文字の各単語をスキャンし*?、と[。これらの文字のいずれかが表示される場合、単語はパターンと見なされ、パターンに一致するファイル名のアルファベット順にソートされたリストに置き換えられます[…]。


1
puttyまたはman'sのテキストレンダリングのいずれかで興味深いバグを見つけました。検索しているテキストが「ワードラップ」された場合、/ searchコマンドはそれを見つけられません。私の端末を最大化したところです:)
Wossname

2
カバーしましたbash。Tho OPは「zshなど」にも興味がありました。
クサラナナンダ

29

一部のシェルで特定のシェルオプションをトリガーしない限り、出力は同じであることが保証されます。

順序はPOSIX標準で指定されています

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

POSIXロケールのLC_COLLATEカテゴリも参照してください。要するに、ifの場合LC_COLLATE=C、物事はASCII順で並べられます。


bashマニュアルには言及します

LC_COLLATE

この変数は、パス名展開の結果を並べ替えるときに使用される照合順序を決定し、パス名展開およびパターンマッチング内の範囲式、等価クラス、および照合シーケンスの動作を決定します。

ksh93そしてzsh彼らはこの点でPOSIX標準に従っていることを信じるように私をリード同様の文言を、持っています。

他のシェルなどpdkshとは、dashファイル名展開の結果ファイル名のソートについては何も言いません。これは、少なくともPOSIXロケールを使用している場合は、依然として同じ標準に準拠していることを意味すると信じたいと思います。私の経験では、ASCIIファイル名のあからさまな「奇妙な」ソートを行うシェルに出くわしていません。


2
並べ替えに影響するnumericglobsortオプションを参照してくださいzshecho *(n)オプションをグローバルに有効にするよりも、グロブ単位で有効にしたいのですが。
ステファンシャゼラス

ひとつまみ。Bashは、デフォルトモードでは、Posixに準拠していません。
fpmurphy

@ fpmurphy1もっと言ってください。
クサラナナンダ

@Kusalananda。BashはPOSIXクレームとして認証されたことはありません。Bashで「POSIX準拠」を取得するには、--posixコマンドラインオプションでBashを呼び出すか、実行する必要がありますset -o posix
-fpmurphy

@ fpmurphy1はい、ただし、ファイル名のグロビング文字の展開のソートは、Bashのposixモードの影響を受けません。gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.htmlを参照してください これにより、ソートがPOSIX準拠であると(むしろ希望して)信じることができます。
クサラナナンダ

1

主要な目標が、入力ファイルを古い順に並べることである場合、次のように書くことができます。

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

また、ローテーションおよび圧縮されたログも含まれる場合:

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

4
ファイルのタイムスタンプは信頼されないことに言及しました。
クサラナナンダ

3
@Kusalananda、そうです、私たちのシステム時間は一般に乱数ジェネレーターとみなされています:)
Wossname
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.